WWW.DISS.SELUK.RU

БЕСПЛАТНАЯ ЭЛЕКТРОННАЯ БИБЛИОТЕКА
(Авторефераты, диссертации, методички, учебные программы, монографии)

 

Pages:     | 1 || 3 | 4 |

«Аннотация В этом документе мы представляем программирование в Scilab1. В первой части мы представляем управление памятью в Scilab. Во второй части мы представляем различные типы данных и анализируем методы ...»

-- [ Страница 2 ] --

Упражнение 3.2 (Запрос типизированных списков) Функции, которые мы рассмотрели, позволяют программировать типизированные списки очень динамичным образом. Мы посмотрим сейчас как использовать функции definedfields для динамического вычисления: определено ли поле, идентифицированное по его строке, или нет. Это позволит получить немного больше практики с типизированными списками. Вспомните, что мы можем создавать типизированные списки без действительного определения значений полей.

Эти поля могут быть определены позже, так что, в отдельный момент времени, мы не знаем были ли все поля определены или нет. Следовательно, нам может потребоваться функция isfielddef, которая бы вела себя как в следующем примере.

-->p = tlist ([ " person "," firstname "," name "," birthyear " ]);

Напишите реализацию функции isfielddef. Чтобы это сделать, вы можете объединить функции find и definedfields.

4 Управление функциями В этом разделе мы рассмотрим управление функциями и представим возможности разработки гибких функций. Мы представляем методы получения информации о функциях и отделения макросов от примитивов. Мы обращаем внимание, что функции не зарезервированы в Scilab’е и предупреждаем об ошибках функции funcprot. Мы представляем использование вызовов, которые позволяет пользователю функции настроить часть алгоритма. Мы анализируем методы разработки функций с переменным числом входных и выходных переменных на основе команд argn, varargin и varargout. Мы покажем общие способы обеспечения значений по умолчанию для входных аргументов. Мы представляем как использовать пустую матрицу [] для преодоления проблемы, вызванной позиционными входными аргументами. Мы также рассматриваем определение функций, где выходные аргументы могут иметь различные типы. Затем мы представляем возможности, которые позволяют разрабатывать надёжные функции. Мы представляем практические примеры для функций error, warning и gettext. Мы представляем модуль parameters, который позволяет решить задачу проектирования функции с большим числом параметров. Детально анализируется обзор переменных через стек вызовов. Ошибки, вызванные плохим использованием этой возможности анализируются на основе типичных случаев использования. Мы представляем ошибки с вызовами и анализируем методы разрешения их. Мы представляем метод, основанный на списках, для обеспечения дополнительных аргументов для вызовов. Наконец, мы представляем инструменты мета-программирования, основанных на функциях execstr и deff.

4.1 Продвинутое управление функциями В этом разделе мы представляем продвинутое управление функциями. В первой части мы представляем различия между макросами и примитивами. Затем мы обращаем внимание на то, что функции не зарезервированы, что подразумевает возможность переопределить функцию, которая уже существует. Мы покажем, что это вызвано тем фактом, что функции являются переменными. Мы представляем функцию funcprot и покажем как использовать вызовы функций.

4.1.1 Как сделать запрос о функции В этом разделе мы представляем различия между макросами и примитивами. Мы анализируем различные значения, возвращённые функциями type и typeof для входных аргументов функций. Мы вводим функцию deff, которая позволяет динамически определять новую функцию на основе строк. Наконец, мы представляем функцию get_function_path, которая возвращает путь к файлу, определяющему макрос.

Есть два главных типа функций:

• макросы, которые написаны на языке Scilab, • примитивы, которые написаны на компилируемом языке, таком, как, например, C, C++ или Fortran.

В большинстве случаев нет прямого способа найти различия между этими двумя типами функций, и эта хорошая черта: когда мы используем функцию, то не заботимся о языке, на котором эта функция написана, поскольку только результат имеет значение. Вот почему мы обычно используем имя function без дополнительных подробностей. Однако, иногда это важно, например, когда мы хотим проанализировать или отладить конкретную функцию: в этом случае необходимо знать откуда функция приходит.

Есть несколько возможностей, которые позволяют запрашивать информацию о конкретной функции. В этом разделе мы сосредотачиваемся на функциях type, typeof, deff и get_function_path.

Тип макроса равен или 11 или 13, а тип примитива равен 130. Эти типы сведены на рисунке 20.

В следующем примере мы определяем функцию командой function. Затем мы вычисляем её тип с помощью функций type и typeof --> function y = myfunction ( x ) --> typeof ( myfunction ) Функция eye является примитивом, который возвращает единичную матрицу. В следующем примере мы упражняемся с функциями type и typeof с входным аргументом eye.

Функция deff позволяет динамически определять функцию, основанную на строках, представляющих её определение. Это позволяет динамически определять новую функцию, например для инкапсуляции одной функции в другую. Функция deff принимает необязательный входной аргумент, который позволяет изменить то, как функция обрабатывается интерпретатором. Такая функция может быть скомпилирована, скомпилирована и профилирована или некомпилирована. Процесс компиляции позволяет формировать более быстрый байт-код, который может быть использован интерпретатором непосредственно без дополнительный операций. Но она так же делает отладку невозможной и поэтому эта возможность может быть отключена. Мы рассмотрим deff более тщательно в разделе 4.7 этого документа По умолчанию функция deff создаёт компилированный макрос. В следующем примере мы определим некомпилированный макрос с помощью функции deff. Первый аргумент функции deff является заголовком функции. В нашем случае функция myplus принимает два входных аргумента y и z и возвращает выходной аргумент x. Второй аргумент функции deff это строка, определяющая тип функции, которую надо создать. Мы можем выбирать между "c" для компилированного макроса, "p" для компилированного и профилированного макроса и "n" для некомпилированного макроса.



--> typeof ( myplus ) Предыдущий пример позволяет проверить что тип некомпилированной функции равен 11.

Когда функция предоставлена в библиотеке, то функция get_function_path позволяет получить путь к функции. Например, модуль оптимизации, предоставляемый Scilab’ом содержит как примитивы (например функцию optim) и макросы (например функцию derivative). Макросы оптимизации собраны в библиотеку optimizationlib, которая анализируется в следующем примере.

--> optimizationlib optimizationlib = Расположение файлов функций: SCI \ modules \ optimization \ macros \.

Предыдущий пример не говорит нам является ли любая из функций, предоставленных optimizationlib, макросом или примитивом. Для этой цели мы можем вызвать функцию typeof, как в следующем примере, который показывает, что функция derivative является макросом.

--> typeof ( derivative ) В следующем примере мы вычисляем путь, ведущий к функции derivative используя функции get_function_path и editor для редактирования этого макроса.

--> get_ function _path ( " derivative " ) D :/ Programs / SCILAB ~1.1 - B \ modules \ optimization \ macros \ derivative. sci --> editor ( get_fun ction_pa th ( " derivative " )) Заметим, что функция get_function_path не работает, когда входной аргумент является функцией, предоставляемой на компилируемом языке, например, функция optim.

--> get_ function _path ( " optim " ) WARNING : " optim " is not a library function 4.1.2 Функции не зарезервированы Можно переопределять функции, которые уже существуют. Часто, это является ошибкой, которая заставляет Scilab формировать сообщение об ошибке. В следующем примере мы определим функцию rand как обычную функцию и проверим, что мы можем вызвать её как любую другую, определённую пользователем, функцию.

Предупреждение : переопределение функции: rand.

Используйте funcprot (0) чтобы не выводить это сообщение Предупреждение о том, что мы переопределили функцию rand должно заставить нас почувствовать себя некомфортно с нашим файлом-сценарием. Оно говорит нам, что функция rand уже существует в Scilab, что может быть легко проверено командой help rand.

Действительно, встроенная функция rand позволяет генерировать случайные числа, и мы, конечно же, не хотим переопределять эту функцию.

В данном случае ошибка очевидна, но практические ситуации могут быть гораздо более сложными. Например, мы можем использовать сложное разветвлённое дерево функций, где функции очень низкого уровня вызывают это предупреждение. Изучая функцию, имеющую ошибку, и запуская её интерактивно в большинстве случаев можно найти ошибку. Более того, поскольку в Scilab’е есть много существующих функций, то вероятно создание новой программы изначально породит конфликт имён. В любом случае, мы должны исправить эту ошибку без переопределения какой-нибудь существующей функции. Мы рассмотрим этот вопрос более детально в следующем разделе, где мы представляем пример, в котором временное отключение защиты функции действительно необходимо. То есть мы представляем функцию funcprot, которая упомянута в сообщении о предупреждении в предыдущем примере.

4.1.3 Функции это переменные В этом разделе мы покажем, что функции является переменными и представляем функцию funcprot.

Мощная возможность языка в том, что функции являются переменными. Это подразумевает то, что мы можем хранить функции в переменных и использовать переменные как функции. В компилируемых языках эта возможность часто называется как указатели на функцию.

В следующем примере мы определяем функцию f. Затем, мы устанавливаем содержимое переменной fp равным функции f. Наконец, мы можем использовать функцию fp как обычную функцию.

Эта возможность позволяет нам использовать широко распространённый инструмент программирования, известный как функции обратного вызова ( callback ).

Функция обратного вызова это указатель функции, который может быть сконфигурирован пользователем компонента. Как только компонент сконфигурирован, он может производить обратный вызов функции, определённой пользователем так, что алгоритм может иметь, по крайней мере частично, настраиваемое поведение.

Поскольку функции являются переменными, мы можем установить значение переменной несколько раз. Фактически, для того, чтобы защитить пользователей от нежелательного переопределения функций, может появляться сообщение-предупреждение, как показано в следующем примере.

Мы начнём с определения двух функций f1 и f2.

Затем мы можем установить переменную f дважды, как в следующем примере.

Предупреждение : переопределение функции: f.

Используйте funcprot (0) чтобы не выводить это сообщение Мы уже видели это предупреждение в разделе 4.1.2, в случае, где мы пытались переопределить встроенную функцию. Но в этой ситуации нет причин защищать себя от установки переменной f в новое значение. К сожалению нет у Scilab’а нет возможности различить эти две ситуации. К счастью есть простой способ отключить на время предупреждение.

Функция funcprot, представленная на рисунке 21, позволяет конфигурировать режим защиты функций.

prot=funcprot() Получить текущий режим защиты функций funcprot(prot) Установить режим защиты функций prot==0 нет сообщений, когда функция переопределена prot==1 предупреждение, когда функция переопределена (по умолчанию) prot==2 ошибка, когда функция переопределена Вообще, не рекомендуется конфигурировать режим защиты функций долговременно.

Например, не следует писать команду funcprot(0) в файле запуска.scilab. Это из-за того, что мы не будет получать предупреждения, и, следовательно, можем использовать файлысценарии, содержащие ошибки, не зная об этом.

Но, для того, чтобы отключить ненужные сообщения предыдущего примера, мы можем временно отключить защиту. В следующем примере мы дублирование режима защиты функции, устанавливаем его временно равным нулю и затем восстанавливаем режим равным дублированному значению.

--> oldfuncprot = funcprot () --> funcprot ( oldfuncprot ) 4.1.4 Функции обратного вызова В этом разделе мы представляем метод управления функциями обратного вызова, то есть, мы рассматриваем случай, где входной аргумент функции сам является функцией.

Например, мы рассматриваем вычисление численных производных конечными разностями.

В следующем примере мы используем встроенную (built-in) функцию derivative для вычисления производной функции myf. Мы сначала определяем функцию myf, которая возводит в квадрат свой входной аргумент x. Затем мы передаём функцию myf функции derivative в качестве обычного аргумента для вычисления численной производной в точке x=2.

Чтобы понять поведение функций обратного вызова, мы должны реализовать нашу упрощённую функцию численной производной, как это сделано в следующем примере. Наша реализация численной производной основана на разложении функции в ряд Тейлора первого порядка в соседстве с точкой x. Она принимает в качестве входных аргументов функцию f, точку x, где численная производная должна быть вычислена и шаг h, который должен использоваться.

function y = myderivative ( f, x, h ) endfunction Подчеркнём, что это пример, который не надо использовать на практике, поскольку встроенная функция derivative гораздо более мощная, чем наша, упрощённая функция myderivative.

Заметим, что в теле функции myderivative входной аргумент f используется как обычная функция.

В следующем примере мы используем переменную myf в качестве входного аргумента функции myderivative.

-->y = myderivative ( myf, 2, 1. e -8 ) Мы сделали предположение, что функция f имеет заголовок y=f(x). Нас может удивить что будет, если это не так.

В следующем примере мы определяем функцию myf2, которая берёт в качестве входных аргументов как x так и a.

endfunction Следующий пример показывает что происходит, если мы попытаемся вычислить численную производную функции myf2.

-->y = myderivative ( myf2, 2, sqrt ( %eps ) ) Неизвестная переменная: a y = myderivative ( myf2, 2, sqrt ( %eps ) ) Мы по-прежнему хотим вычислить численную производную нашей функции, даже если она имеет два аргумента. Мы увидим в разделе 4.5.1 как обзор переменных может быть использован, чтобы позволить функции myf2 узнать о величине аргумента a. Метод программирования, который мы рассмотрим не считается чисто программной практикой.

Вот почему мы представим в разделе 4.6.4 метод управления функциями обратного вызова с дополнительными аргументами.

4.2 Разработка гибких функций В этом разделе мы представляем разработку функций с переменным числом входных и выходных переменных. Этот раздел обозревает функцию argn и переменные varargin и varargout. Как мы увидим, указание переменного числа входных аргументов позволяет упростить использование функции, давая значения по умолчанию для аргументов, которые не устанавливаются пользователем.

Функции, относящиеся к этому вопросу представлены на рисунке 22.

Возвращает текущее число входных и выходных аргументов.

varargout Список, хранящий выходные аргументы.

Рис. 22: Функции, имеющие отношение к переменному числу входных и выходных переменных.

В первой части мы анализируем простой пример, который позволяет нам видеть в действии функцию argn. Во второй части мы рассматриваем реализацию функции, которая вычисляет численные производные данной функции. Затем мы описываем как решить проблему, порождённую упорядоченными входными переменными, с помощью особого использования синтаксиса пустой матрицы. В заключительной части мы представляем функцию, чьё поведение зависит от типа её входных аргументов.

4.2.1 Обзор функции argn В этом разделе мы делаем обзор функции argn и переменных varargin и varargout. Мы также представляем простую функцию, которая позволяет понять как выполняются эти функции.

Мы начинаем анализировать основы управления функций с переменным числом входных и выходных аргументов. Типичный заголовок таких функций следующий.

function varargout = myargndemo ( varargin ) Аргументы varargout и varargin являются списками list, которые представляют входные и выходные аргументы. В телу определения функции, мы вызываем функцию argn, которая формирует две следующих выходных переменных.

• lhs, число выходных переменных, • rhs, число входных переменных.

Переменные varargin и varargout определяются во время вызова функции. Число элементов в этих двух списках следующее.

• На входе число элементов в varargin равно rhs.

• На входе число элементов в varargout равно нулю.

• На выходе число элементов в varargout должно быть lhs.

Действительное число элементов в varargin зависит от аргументов, предоставленных пользователем. В противоположность этому список varargout всегда пуст на входе, а задача определения его содержимого оставлена на функцию.

Давайте теперь рассмотрим следующую функцию myargndemo, которая сделает эту тему более практичной. Функция myargndemo отображает число выходных аргументов lhs, число входных аргументов rhs и число элементов в списке varargin. Затем мы установим количество выходных аргументов в varargout равным 1.

function varargout = myargndemo ( varargin ) В нашей функции мы просто копируем входные аргументы в выходные аргументы.

Следующий пример показывает как функция работает, когда её вызывают.

lhs =1, rhs =0, length ( varargin )= --> myargndemo (1);

lhs =1, rhs =1, length ( varargin )= --> myargndemo (1,2);

lhs =1, rhs =2, length ( varargin )= --> myargndemo (1,2,3);

lhs =1, rhs =3, length ( varargin )= lhs =1, rhs =1, length ( varargin )= lhs =2, rhs =1, length ( varargin )= lhs =3, rhs =1, length ( varargin )= В предыдущем примере показано, что число элементов в списке varargin равно rhs.

Заменим, что минимальное число выходных аргументов равно 1, так что мы всегда имеем lhs 1. Это свойство интерпретатора Scilab, который заставляет функцию всегда возвращать по крайней мере один выходной аргумент.

Подчеркнём, что функция может быть определена как с varargin, так и с varargout, только с varargin или только с varargout. Это решение остаётся за разработчиком функции.

Функция myargndemo может быть вызвана с любым числом аргументов. На практике не все вызываемые последовательности будут разрешены, так что мы будем должны вводить команды ошибки, которые будут ограничивать использование функции. Эта тема будет рассмотрена в следующем разделе.

4.2.2 Практические вопросы Теперь вернёмся к более практичному примеру, который включает в себя вычисление численных производных. Этот пример довольно сложен, но представляет ситуацию, где число входных и выходных аргументов переменное.

Функция derivative в Scilab позволяет вычислить первую (якобиан) и вторую (гессиан) производные функции со многими параметрами. Для того, чтобы сделать обсуждение этой темы более жизненной, мы рассмотрим проблему внедрения упрощённой версии этой функции. Реализация функции derivative основана на конечных разностях и использует формулы конечных разностей различных порядков (подробнее на эту тему см. в [47]).

Будем полагать, что функция гладкая и что относительная ошибка в вычислении функции равна машинной точности 1016. Общая ошибка, которая связана с формулой конечных разностей, это сумма ошибок усечения (поскольку используется ограниченной число членов в разложении в ряд Тейлора) и ошибки округления (из-за ограниченной точности вычислений с плавающей запятой в вычислении функции). Следовательно, оптимальный шаг, который минимизирует общую ошибку может быть однозначно вычислен в зависимости от машинной точности.

Следующая функция myderivative1 позволяет вычислять первую и вторую производные функции одной переменной. Её входными аргументами являются функция для дифференцирования f и вычисления производной в точке x, шаг h и порядок формулы order.

Функция обеспечивает прямую формулу первого порядка и центрированную формулу второго порядка. Выходными аргументами являются значение первой производной fp и второй производной fpp. В комментариях функции мы написали оптимальный шаг в качестве памятки.

function [ fp, fpp ] = myderivative1 ( f, x, order, h ) endfunction Теперь проанализируем поведение функции myderivative1 на практике. Мы раскроем, что эта функция имеет несколько изъянов и отсутствие гибкости и производительности.

Рассмотрим вычисление численной производной функции косинуса в точке x = 0.

Определим функцию myfun, которая вычисляет косинус её входного аргумента x.

endfunction В следующем примере мы используем функцию myderivative1 для того, чтобы вычислить первую производную косинуса.

--> fp = myderivative1 ( myfun, x0, 1, %eps ^(1/2) ) --> fp = myderivative1 ( myfun, x0, 2, %eps ^(1/3) ) Точное значение первой производной равно sin(/6) = 1/2. Мы видим, что центрированная формула, связанная с порядком order=2, как и ожидалось, более точная, чем формула порядка order=1.

В следующем примере мы вызываем функцию myderivative1 и вычисляем первую и вторую производные косинуса.

- - >[ fp, fpp ] = myderivative1 ( myfun, x0, 1, %eps ^(1/2) ) - - >[ fp, fpp ] = myderivative1 ( myfun, x0, 2, %eps ^(1/3) ) Точное значение второй производной fpp равно cos(/6) = 3/2 8,6602·101. И снова мы видим, что формула второго порядка более точная, чем формула первого порядка.

Мы проверили, что наша реализация верна. Теперь проанализируем разработку функции и введём необходимость в переменном числе аргументов.

Функция myderivative1 имеет три недостатка.

• Она может делать ненужные вычисления, что является проблемой производительности.

• Она может давать неточные результаты, что является проблемой точности.

• Она не позволяет использовать значения по умолчанию для order и h, что является проблемой гибкости.

Проблема производительности вызвана тем фактом, что два выходных аргумента fp и fpp вычисляются даже если пользователь функции на самом деле не требует второй, необязательный, выходной аргумент fpp. Заметим, что вычисление fpp требует несколько дополнительных вычислений функции в сравнении с вычислениями fp. Это подразумевает, что если требуется только выходной аргумент fp, то выходной аргумент по-прежнему будет вычисляться, что бесполезно. Точнее, если используется вызывающая последовательность:

то, даже если fpp не является выходным аргументом, внутри переменная fpp по-прежнему будет вычисляться.

Проблема точности вызывается тем фактом, что оптимальный шаг может быть использован либо для вычисления первой производной, либо для вычисления второй производной, но не обеих. Действительно, оптимальный шаг разный для первой и для второй производной.

Точнее, если используется вызывающая последовательность:

[ fp, fpp ] = myderivative1 ( myfun, x0, 1, %eps ^(1/2) ) то шаг %eps(1/2) является оптимальным для первой производной fp. Но, если используется вызывающая последовательность:

[ fp, fpp ] = myderivative1 ( myfun, x0, 1, %eps ^(1/3) ) тогда шаг %eps(1/3) является оптимальным для второй производной fpp. Во всех случаях мы должны выбирать и не имеем хорошей точности для первой и второй производных.

Проблема гибкости вызвана тем фактом, что пользователь должен определять как order, так и h входными аргументами. Проблема в том, что пользователь может не знать какое значение использовать для этих параметров. Это особенно очевидно для параметра h, который связан с проблемами плавающей запятой, которые могут быть совершенно неизвестны пользователю. Следовательно, было бы удобно, если мы бы мы могли использовать значения этих входных переменных по умолчанию. Например, мы можем захотеть использовать значение по умолчанию order=2, поскольку оно даёт более точные значения производной с тем же количеством выходных значений функции. Задавая порядок, оптимальный шаг h может быть вычислен с помощью одной из оптимальных формул.

Теперь, когда мы знаем какие проблемы встречаются с нашей myderivative1, мы проанализируем реализацию, основанную на переменном количестве входных и выходных переменных.

4.2.3 Использование переменных аргументов на практике Целью этого раздела заключается в том, чтобы предоставить реализацию функции, которая позволяет использовать следующие вызывающие последовательности.

Главным преимуществом этой реализации являются • возможно задать значения по умолчанию для order и h, • вычислять fpp только если она задана пользователем, • использовать оптимальный шаг h как для первой, так и для второй производной, если это не указано пользователем.

Следующая функция myderivative2 содержит алгоритм конечной разности с оптимальным порядком myderivative2 и шагом h.

1 \ lstset { language = scilabscript } 3 \ begin { lstlisting } 4 function varargout = myderivative2 ( varargin ) 8 " %s : Ожидалось от %d до %d входных аргументов, но указано %d ",..

13 " %s : Ожидалось от %d до %d выходных аргументов, но указано %d ",..

40 varargout (1) = fp 55 endfunction Поскольку тело этой функции довольно сложное, то мы сейчас проанализируем его основные части.

Строчка №1 определяет функцию, как берущую переменную varargin в качестве входного аргумента, и переменную varargout в качестве выходного аргумента.

Строчка №2 использует функцию argn, которая возвращает настоящее число входных и выходных аргументов. Например, когда используется вызывающая последовательность fp = myderivative2 ( myfun, x0 ), то мы имеем rhs=2 и lhs=1. Следующий пример представляет различные возможные вызывающие последовательности.

Строчки с №2 по №12 добавлены для проверки того, что число входных и выходных аргументов правильное. Следующий пример покажет ошибку, которая формируется в случае, когда пользователь ошибочно вызовет функцию с 1 входным аргументом --> fp = myderivative2 ( myfun ) myderivative2 : Ожидалось от 2 до 4 входных аргументов, но указано fp = myderivative2 ( myfun ) Строчки №13 и №14 показывают как напрямую установить значения входных аргументов f и x, которые всегда должны быть в вызывающей последовательности. Строчки с № по №19 позволяют установить значения параметра order. Когда число входных аргументов rhs более 3, то мы делаем вывод, что значение переменной order задано пользователем, и мы используем это значение напрямую. В противном случае, мы устанавливаем значение этого параметра по умолчанию, то есть мы устанавливаем order=2.

Тот же самый процесс проведён в строчках с №20 по №25 для того, чтобы установить значение h. Более того, мы устанавливаем значение логической переменной hflag. Эта переменная устанавливается равной %t, если пользователь указал h и в %f, если нет.

Строчки с №26 по №36 позволяют вычислить первую производную, а вторые производные вычисляются в строчках с №38 по №51. В каждом случае, если пользователь не укажет h, то есть, если hflag установлен в ложь, то используется оптимальный шаг.

Число выходных переменных должно быть больше или равно одному. Следовательно, не нужно проверять значение lhs перед установкой varargout(1) в строчке №37.

Один важный момент заключается в том, что вторая производная fpp вычисляется только если это потребуется пользователю. Это гарантируется в строчке №38, которая проверяет количество выходных переменных: вторая производная вычисляется только если второй выходной аргумент fpp был действительно записан в вызывающей последовательности.

Следующий пример показывает простое использование функции myderivative2, где мы использовали значение по умолчанию как для order, так и h.

--> fp = myderivative2 ( myfun, x0 ) Заметим, что в этом случае вторая производная не вычислялась, что может сохранить значительное количество времени.

В следующем примере мы установили значение order и использовали значение по умолчанию h.

--> fp = myderivative2 ( myfun, x0, 1 ) Мы можем также вызвать эту функцию с двумя выходными аргументами, как в следующем примере.

- - >[ fp, fpp ] = myderivative2 ( myfun, x0, order ) Заметим, что в этом случае оптимальный шаг h использовался как для первой, так и для второй производной.

В этом разделе мы видели как управлять числом переменных входных и выходных аргументов.

Но, в некоторых ситуациях, этого не достаточно и по-прежнему имеются ограничения. Например, метод, который мы представили ограничен порядком аргументов, то есть, их положением в вызывающей последовательности. В самом деле, функция myderivative не может быть вызвана использованием значений по умолчанию для аргументов order и установкой настраиваемых значений для аргумента h. Это ограничение вызвано порядком аргументов: входной аргумент h идёт после аргумента order. На практике удобно, если можно использовать значение по умолчанию для аргумента №i, и по-прежнему устанавливать значение аргумента №i + 1 (или любого другого аргумента в правой части вызывающей последовательности). Следующий раздел позволяет решить этот вопрос.

4.2.4 Значения по умолчанию для необязательных аргументов В этом разделе мы опишем как управлять необязательными аргументами со значениями по умолчанию. Мы покажем как решить проблему, порождённую упорядоченными входными переменными с помощью специального использования синтаксиса пустых матриц [].

Действительно, мы видели в прошлом параграфе, что основное управление необязательными аргументами запрещает нам устанавливать входной аргумент №i + 1 и использовать значение по умолчанию для входного аргумента №i. В этом разделе мы покажем метод, где пустая матрица используется для представления параметра по умолчанию.

Давайте рассмотрим функцию myfun, которая принимает x, p и q в качестве входных переменных и возвращает выходной аргумент y.

Если мы установим все x, p и q в качестве входных аргументов, то функция работает превосходно, как в следующем примере.

Сейчас наша функция myfun не очень гибкая, так что, если мы передадим только один или два аргумента, то функция вызовет ошибку.

Неизвестная переменная: q Неизвестная переменная: q Было бы удобно, если бы, например, параметр p имел по умолчанию значение 2, а параметр p имел по умолчанию значение 1. В этом случае, если ни p ни q не указаны, то команда foo(3) должна вернуть 9.

Мы можем использовать переменную varargin для того, чтобы иметь переменное число входных переменных. Но, используя напрямую, это не позволит установить третий аргумент и использовать значение по умолчанию для второго аргумента. Эта проблема вызвана упорядочиванием входных переменных. Для того, чтобы решить эту проблему, мы будем по-особенному использовать пустую матрицу [].

Преимущество этого метода заключается в возможности использовать значение по умолчанию аргумента p, при этом устанавливать значение аргумента q. Для того, чтобы информировать функцию, что особый аргумент должен быть установлен равным значению по умолчанию, мы устанавливаем его равным пустой матрицу. Эта ситуация представлена в следующем примере, где мы устанавливаем аргумент p равным пустой матрице.

--> myfun2 (2,[],3) // то же, что и myfun2 (2,2,3) Для того, чтобы определить функцию, мы используем следующий метод. Если входной аргумент не указан, или если он указан равным пустой матрице, то мы используем значение по умолчанию. Если аргумент указан и отличается от пустой матрицы, то мы используем непосредственно его. Этот метод представлен в функции myfun2.

1 function y = myfun2 ( varargin ) 4 msg = gettext ( " %s : Wrong number of input arguments : %d to %d expected.\ n " ) 18 qdefault = 29 endfunction С точки зрения алгоритма мы могли бы выбрать другое опорное значение, отличное от пустой матрицы. Например, мы могли бы рассматривать пустую строку или любое другое особое значение. Причина, почему пустая матрица предпочитается на практике состоит в том, что сравнение с пустой матрице быстрее, чем сравнение с любым другим особым значением. Действительное содержимое матрицы не имеет значения, поскольку сравнивается только размер матрицы. Следовательно, накладные расходы на производительность из-за управления значениями по умолчанию малы как только можно.

Конечно, мы по-прежнему можем использовать необязательные аргументы как обычно, как это показано ниже.

На практике этот метод и гибок и надёжен, с основным управлением входных переменных, которые по-прежнему главным образом зависят от их порядка.

Этот метод по-прежнему ограничен, если число входных переменных велико. В этом случае существуют другие решения, которые позволяют использовать упорядоченные аргументы.

В разделе 4.4 мы анализируем модуль parameters, который позволяет конфигурировать неупорядоченный набор входных аргументов отдельно из действительного использования параметров. Другое решение заключается в том, чтобы имитировать объектно-ориентированное программирование, как показано в разделе 3.7.

4.2.5 Функции с переменным типом входных аргументов В этом разделе мы представляем функцию, чьё поведение зависит от типа её входных аргументов.

Следующая функция myprint указывает особое отображение для матрицы значений типа double и другое отображение для матрицы логических значений. Тело функции основано на команде if, которая переключает различные части исходного кода в зависимости от типа переменной X.

disp ( "Матрица логических значений" ) В следующем примере мы вызываем функцию myprint с двумя разными типами матриц.

Матрица значений типа double Матрица логических значений Мы можем сделать предыдущую функцию более полезной, определив отдельно правила форматирования для каждого типа данных. В следующей функции мы группируем три значения, отделённых большим пропуском, который визуально создаёт группы переменных.

// Вещественная или комплексная матрица // Матрица логических значений error ( "Неожиданный тип входного аргумента" ) endfunction Следующий пример показывает использование предыдущей функции. Сначала отобразим матрицу значений типа double.

Затем отобразим матрицу логических значений.

TTF FFF

TFF TTT

FTF FTF

Многие встроенные функции разработаны на этом принципе. Например, функция roots возвращает корни многочлена. Её входные аргументы могут быть либо многочленом либо матрицей значений типа double, представляющих его коэффициенты. На практике, возможность управлять разными типами переменных в одной функции даёт дополнительный уровень гибкости, который гораздо сложнее получить в компилируемых языках, таких как Си или Фортран.

4.3 Устойчивые функции В этом разделе мы представляем некоторые правила, которые можно применять ко всем функциям которые разрабатываются так, чтобы быть устойчивыми при неправильном использовании. В следующем разделе мы представим функции warning и error, которые являются основой разработки устойчивых функций. Затем мы представим общую схему работы для проверок, используемых внутри устойчивых функций. Наконец мы представим функцию, которая вычисляем матрицу Паскаля и покажем как применять эти правила на практике.

4.3.1 Функции warning и error Часто случается, что входные аргументы функции могут иметь только ограниченное число возможных значений. Например, мы могли бы потребовать, чтобы данное входное число было положительным, или что данное число с плавающей запятой могло иметь только три возможных значения. В этом случае мы можем использовать функции error или warning, которые представлены на рисунке 23. Функция gettext относится к локализации и описана позднее в этом разделе.

Посылает сообщение об ошибке и останавливает вычисление.

warning Посылает сообщение-предупреждение.

gettext Получает текст, переведённый на местный язык.

Рис. 23: Функции, относящиеся к сообщениям об ошибке.

Теперь дадим пример, который показывает как эти функции могут использоваться для защиты пользователя функции от неправильного использования. В следующем примере мы определяем функцию mynorm, которая является упрощённой версией встроенной функции norm. Наша функция mynorm позволяет вычислить 1-, 2- или -норму вектора. В противном случае наша функция не определена и поэтому формируется ошибка.

В следующем примере мы проверяем выходные значения функции mynorm, когда входной аргумент n равен 1, 2, inf и неожиданному значению 12.

mynorm : Invalid value 12 for n.

Входной аргумент функции error является строкой. Мы могли бы использовать более простые сообщения, такие как "Неверное значение n.", например. Наше сообщение немного более сложное по нескольким причинам. Цель дать пользователю полезную обратную связь, когда формируется ошибка в как можно более глубоком звене цепи вызовов. Для того, чтобы так сделать, мы даём как можно больше информации о происхождении ошибки.

Во-первых, это удобно для пользователя, что его информируют о том, какое точно значение было отклонено алгоритмом. Следовательно, мы включаем действующее значение входного аргумента n в сообщение. Более того, мы делаем так, чтобы первой строкой сообщения об ошибке было название функции. Это позволяет немедленно получить имя функции, сформировавшей ошибку. В этом случае используется функция msprintf для форматирования строки и формирования переменной msg, которая передаётся функции error.

Мы можем сделать так, что сообщение об ошибке можно было перевести на другие языки, если необходимо. В самом деле, Scilab локализован так, что большинство сообщений появятся на языке пользователя. Следовательно, мы получим сообщения на английском в США и Великобритании, на французском во Франции и т. д. Для того, чтобы это сделать, мы можем использовать функцию gettext, которая возвращает переведённую строку, на основе базы данных локализации. Это приведено на следующем примере.

localstr = gettext ( " %s : Invalid value %d for n. " ) Заметьте, что строка, которая передаётся в функцию gettext, является не выходным, а входным аргументом функции msprintf. Это из-за того, что система локализации предоставляет карту из строки "%s: Invalid value %d for n." в локализованные строки, такие как, например, французское сообщение "%s: Valeur %d invalide pour n.". Эти локализованные сообщения хранятся в файлах данных.pot. Больше информации о локализации найдёте в [29].

Есть случаи, когда мы хотим сформировать сообщение, но не хотим прерывать вычисление. Например, мы хотим информировать нашего пользователя, что функция mynorm несколько хуже по сравнению со встроенной функцией norm. В этом случае мы не хотим прерывать алгоритм, поскольку вычисление правильное. Мы можем использовать функцию warning, как в следующем примере.

1 function y = mynorm2 ( A, n ) 2 msg = msprintf ( " %s : Please use norm instead. "," mynorm2 " ) 11 msg = msprintf ( " %s : Invalid value %d for n. "," mynorm2 ",n ) 14 endfunction Следующий пример показывает результат работы функции.

ВНИМАНИЕ: mynorm2 : Please use norm instead.

4.3.2 Общая схема для проверок входных аргументов Устойчивая функция должна защищать пользователя от неправильного использования функции. Например, если функция принимает матрицу значений типа double в качестве входного аргумента, мы можем получить сообщения об ошибке, которые далеко не ясные.

Мы можем даже вообще не получить никаких сообщений от ошибке, например, если функция рушится тихо. В этом разделе мы представляем общую схему для проверок в устойчивых функциях.

Следующая функция pascalup_notrobust это функция, которая возвращает верхнюю треугольную матрицу Паскаля P в зависимости от размера матрицы n.

Далее мы вычислим верхнюю треугольную матрицу Паскаля размером 5 5.

Функция pascalup_notrobust не делает никаких проверок входного аргумента n. В случае, когда входной аргумент отрицательный или не является числом с плавающей запятой, функция не формирует никакого сообщения от ошибке.

Это поведение не должно считаться нормальным, поскольку она рушится тихо. Следовательно, пользователь может указать неверные входные аргументы функции и даже не заметить что произошло что-то неправильное.

Вот почему мы часто проверяем входные аргументы функций так, чтобы формировались как можно более понятные для пользователя сообщения об ошибке. Вообще, мы должны рассматривать следующие проверки:

• число входных/выходных аргументов • тип входных аргументов, • размер входных аргументов, • содержание входных аргументов, которые могут быть сокращены в виде число/тип/размер/содержание. Эти правила следует включать как часть нашего стандартного способа написания публичных функций.

4.3.3 Пример устойчивой функции В этом разделе мы представляем пример устойчивой функции, которая вычисляет матрицу Паскаля. Мы представляем примеры, где проверки, которые мы используем, полезны для обнаружения неверного использования функции.

Функция pascalup является улучшенной версией со всеми необходимыми проверками.

function P = pascalup ( n ) // Проверка числа аргументов [ lhs, rhs ] = argn () lstr = gettext ( " %s : Wrong number of input arguments : %d to %d expected, but %d provided. " ) error ( msprintf ( lstr, " pascalup ",1,1, rhs )) // Проверка типа аргументов if ( typeof ( n ) " constant " ) then lstr = gettext ( " %s : Wrong type for input argument # %d : %s expected, but %s provided. " ) error ( msprintf ( lstr, " pascalup ",1, " constant ", typeof ( n ))) // Проверка размера аргументов lstr = gettext ( " %s : Wrong size for input argument # %d : %d entries expected, but %d provided. " ) error ( msprintf ( lstr, " pascalup ",1,1, size (n, " * " ))) // Проверка содержания аргументов lstr = gettext ( " %s : Wrong content for input argument # %d : complex numbers are forbidden. " ) error ( msprintf ( lstr, " pascalup ",1)) lstr = gettext ( " %s : Wrong content for input argument # %d : positive entries only are expected. " ) error ( msprintf ( lstr, " pascalup ",1)) lstr = gettext ( " %s : Wrong content of input argument # %d : argument is expected to be a flint. " ) error ( msprintf ( lstr, " specfu n_pasc al ",1)) for i = 2:( n -1) endfunction В следующем примере мы запускаем функцию pascalup и формируем различные сообщения об ошибках.

--> pascalup ( ) ! - - error pascalup : Wrong number of input arguments : 1 to 1 expected, but 0 provided.

at line 8 of function pascalup called by :

pascalup ( ) --> pascalup ( -1 ) ! - - error pascalup : Wrong content for input argument #1: positive entries only are expected.

at line 33 of function pascalup called by :

pascalup ( -1 ) --> pascalup ( 1.5 ) ! - - error spe cfun_p ascal : Wrong content of input argument #1: argument is expected to be a flint.

at line 37 of function pascalup called by :

pascalup ( 1.5 ) Правила, которые мы представили, используются в большинстве макросов Scilab. Многочисленные эксперименты доказали, что эти методы предоставляют улучшенную устойчивость, так что пользователи с меньшей вероятностью будут использовать функции с неверными входными аргументами.

4.4 Использование parameters Целью модуля parameters является возможность разработки функций, которые имеют, возможно, большое количество необязательных параметров. Использование этого модуля позволяет избежать проектирование функции с большим числом входных аргументов, что может привести к путанице. Этот модуль был введён в Scilab версии 5.0, после работы Янна Коллетта (Yann Collette) по объединению алгоритмов оптимизации, таких как алгоритмы генетики и имитация отжига. Функции модуля parameters представлены на рисунке 24.

add_param Получить значение параметра из списка параметров get_param Инициализировать структуру, которая будет управлять списком параinit_param Проверяет, есть ли в наличии параметр, представленный в списке параis_param Перечислить все имена параметров в списке параметров list_param remove_param Удалить параметр и связанное с ним значение из списка параметров Установить значение параметра в списке параметров set_param В первой части мы делаем обзор модуля и описываем его прямое использование. Во второй части мы представляем практический пример, основанный на алгоритме сортировки. Последняя часть концентрируется на безопасном использовании модуля, защищающем пользователя от общих ошибок.

4.4.1 Обзор модуля В этом разделе мы представляем модуль parameters и даём пример его использования в контексте алгоритма слияния-сортировки. Точнее, мы представляем функции init_param, add_param и get_param.

Модуль основан на установке связи ключей и значений.

• Каждый ключ соответствует полю списка параметров и сохраняется как строковая переменная. Список доступных ключей определён разработчиком функции. Нет ограничений на количество ключей.

• Тип каждого значения зависит от особых требований алгоритма. На самом деле любой тип значения может быть сохранён в структуре данных, включая (но не ограничиваясь) матрицу чисел типа double, строковые переменные, многочлены и т. д.

Следовательно, эта структура данных чрезвычайно гибка, как мы и увидим далее.

Для того, чтобы понять модуль parameters, мы можем выделить ситуации между двумя точками зрения:

• точка зрения пользователя, • точка зрения разработчика.

Следовательно, в дальнейшем обсуждении мы будем представлять первую, которая имеет место, когда мы хотим использовать модуль parameters для того, чтобы вызвать функцию.

Затем мы обсудим чт разработчик должен сделать для того, чтобы управлять необязательо ными параметрами.

Для того, чтобы сделать обсуждение как можно более применимым, мы возьмём пример алгоритма сортировки и изучим его в этом разделе. Мы рассматриваем функцию mergesort, которая определяет алгоритм сортировки, основанный на комбинации рекурсивной сортировки и слияния. Функция mergesort связана со следующими вызывающими последовательностями где x матрица значений типа double, которые необходимо рассортировать, params список параметров, и y матрица рассортированных значений типа double. Действительная реализация функции mergesort будет определена позднее в этом разделе. Переменная params это список параметров, который определяет два параметра:

• direction, логическое значение, истина для возрастающего порядка и ложь для убывающего порядка (по умолчанию direction = %t), • compfun, функция, которая определяет функцию сравнения (по умолчанию compfun = compfun_default).

Функция compfun_default функция сравнения по умолчанию, будет представлена позднее в этом разделе.

Сначала мы рассмотрим точку зрения пользователя и представим пример использования функций из модуля parameters. Мы хотим отсортировать матрицу чисел в порядке возрастания. В следующем примере, мы вызываем функцию init_param, которая создаёт пустой список параметров params. Затем, мы добавим ключ "direction" к списку параметров. Наконец, мы вызовем функцию mergesort с переменной params в качестве второго входного аргумента.

params = init_param ();

params = add_param ( params, " direction ", %f );

Теперь рассмотрим точку зрения разработчика и проанализируем команды, которые могут быть использованы в теле функции mergesort. В следующем примере мы вызываем функцию get_param для того, чтобы получить значения, соответствующие ключу "direction". Мы передаём значение %t функции get_param, которое является значением, используемым по умолчанию в случае, когда этот ключ не определён.

--> direction = get_param ( params, " direction ", %t ) Поскольку ключ "direction" определён и является ложью, то мы просто получаем назад наше значение. Мы можем использовать расширенную вызывающую последовательность функции get_param с выходным аргументом err. Переменная err равна истине, если во время обработки аргумента произошла ошибка.

- - >[ direction, err ] = get_param ( params, " direction ", %t ) В нашем случае никакой ошибки не произошло, поэтому переменная err имеет значение ложь.

Мы можем анализировать другие комбинации событий. Например, давайте рассмотрим ситуацию, когда пользователь определяет только пустой список параметров, как показано ниже.

params = init_param ();

В этом случае, в теле функции mergesort прямой вызов функции get_param формирует предупреждение.

--> direction = get_param ( params, " direction ", %t ) ВНИМАНИЕ: get_param : параметр direction не определен Предупреждение указывает, что ключ "direction" не определён, так что возвращается значение по умолчанию %t. Если мы используем дополнительный выходной аргумент err, то предупреждений больше нет, но переменная err становится истиной.

- - >[ direction, err ] = get_param ( params, " direction ", %t ) Как и в последнем примере, давайте рассмотрим случай, когда пользователь определяет переменную params как пустую матрицу.

Как мы увидим позднее в этом разделе, этот случай может произойти когда мы хотим использовать значения по умолчанию. Следующий пример показывает, что ошибка формируется при вызове функции get_param.

--> direction = get_param ( params, " direction ", %t ) get_param : Неверный тип входного параметра №1: ожидался plist.

direction = get_param ( params, " direction ", %t ) Действительно, ожидается, что переменная params является plist. Это из-за того, что функция init_param создаёт переменные с типом plist, который стоит для списка параметров.

Фактически, действительная реализация функции init_param создаёт типизированный список с типом plist. Для того, чтобы предыдущая ошибка не формировалась, мы можем использовать выходной аргумент err как в следующем примере.

- - >[ direction, err ] = get_param ( params, " direction ", %t ) Мы видим, что переменная err во время обработки переменной params. Как и прежде, в этом случае функция get_param возвращает значение по умолчанию.

4.4.2 Практический случай В этом разделе мы рассматриваем действительное применение функции mergesort, представленной в предыдущем разделе. Алгоритм сортировки, который мы собираемся представлять, основан на алгоритме, который Бруно Пинсьон (Bruno Pinon) разработал в Scilab [45].

Модификации, включённые в реализацию, представленную в этом разделе, включают в себя управление необязательными аргументами "direction" и "compfun". Заинтересованные читатели могут найти много подробностей об алгоритме сортировки в [28].

Следующая функция mergesort даёт алгоритм сортировки, основанный на методе слияния-сортировки. Её первый аргумент, x это матрица значений типа double, которые нужно сортировать. Второй аргумент, params, необязательный и представляет список параметров. Мы делаем первый вызов функции get_param для того, чтобы получить значение параметра direction с %t в качестве значения по умолчанию. Затем мы получаем значение параметра compfun с compfun_default в качестве значения по умолчанию. Функция compfun_default определяется позже в этом разделе. Наконец, мы вызываем функцию mergesortre, которая реализует на практике рекурсивный алгоритм слияния-сортировки.

function y = mergesort ( varargin ) x1 = mergesortre ( x (1: m ), direction, compfun ) endfunction Заметим, что функция mergesorte не вызывает саму себя. Вместо этого, функция mergesortre, которая на самом деле выполняет алгоритм рекурсивно, имеет фиксированное количество входных аргументов. Это позволяет уменьшить накладные расходы, вызванные обработкой необязательных переменных.

Следующая функция merge объединяет свои два отсортированных входных аргумента x1 и x2 в свой отсортированный выходной аргумент x. Операция сравнения выполняется менной order, так, чтобы порядок стал убывающим вместо возрастающего порядка по умолчанию.

function x = merge ( x1, x2, direction, compfun ) endfunction Следующая функция compfun_default является функцией сравнения по умолчанию.

Она возвращает order=-1 если xy.

function order = compfun_default ( x, y ) endfunction Теперь проанализируем поведение функции mergesort вызывая её с особыми входными параметрами. В следующем примере мы сортируем матрицу scivarx, содержащую целый числа с плавающей запятой в порядке возрастания.

В следующем примере мы устанавливаем параметр direction в %f, так что порядок сортированной матрицы убывающий.

--> params = init_param ();

--> params = add_param ( params, " direction ", %f );

--> mergesort ( x, params ) ’ Теперь мы хотим настроить функцию сравнения так, чтобы мы могли менять порядок сортированной матрицы. В следующем примере мы определяем функцию сравнения mycompfun, которая позволяет разделять чётные и нечётные целые числа с плавающей запятой.

function order = mycompfun ( x, y ) if ( modulo (x,2) == 0 & modulo (y,2) == 1 ) then elseif ( modulo (x,2) == 1 & modulo (y,2) == 0 ) then // 1 params = add_param ( params, " compfun ", mycompfun );

Мы не настроили параметр direction, так что был использован порядок по умолчанию возрастающий. Как мы можем видеть в выходном аргументе, чётные числа, как ожидалось, идут перед нечётными.

4.4.3 Проблемы с модулем parameters У модуля parameters есть проблема, которая может заставить пользователя рассматривать значения по умолчанию вместо ожидаемых. Модуль parameters не защищает пользователя от неожиданных полей, что может произойти, если мы сделаем ошибку в имени ключа при настройке параметра. В этом разделе мы покажем как проблема может появиться с точки зрения пользователя. С точки зрения разработчика мы представляем функцию check_param, которая позволяет защитить пользователя от использования ключа, который не существует.

В следующем примере мы вызываем функцию mergesort с неверным ключом "compffun" вместо "compfun".

--> params = init_param ();

--> params = add_param ( params, " compffun ", mycompfun );

Мы видим, что наш неправильный ключ "compffun" был проигнорирован, так что использовалась функция сравнения по умолчанию: матрица была просто отсортирована в возрастающем порядке. Причина в том, что модуль parameters не проверяет, что ключ "compffun" не существует.

Поэтому мы предлагаем использовать следующую функцию check_param, которая принимает в качестве входных аргументов список параметров params и матрицу строчных элементов allkeys. Переменная allkeys хранит список всех ключей, которые доступны в списке параметров. Алгоритм проверяет, что каждый ключе в params существует в allkeys. Выходные аргументы логическая noerr и строковая msg. Если нет ошибки, то переменная noerr истина и строковая msg пустая матрица. Если один ключ params не найден, мы устанавливаем noerr в значение ложь и формируем сообщение об ошибке. Это сообщение об ошибке используется для действительного формирования ошибки только если выходной аргумент msg не использовался в вызывающей последовательности.

function [ noerr, msg ] = check_param ( params, allkeys ) gettext ( " %s : Wrong type for input argument # %d : %s expected.\ n " ),..

gettext ( " %s : Wrong type for input argument # %d : %s expected.\ n " ),..

currkeys = getfield (1, params ) gettext ( " %s : Unexpected key " " %s " " in parameter list. " ),..

endfunction Следующая версия функции mergesort использует функцию check_param для того, чтобы проверить, что список доступных ключей является матрицей ["directioncompfun"].

function y = mergesort ( varargin ) if ( rhs params = init_param ();

--> params = add_param ( params, " compffun ", mycompfun );

--> mergesort ( [4 5 1 6 2 3] ’, params ) check_param : Unexpected key " compffun " in parameter list.

at line 75 of function check_param called by :

С нашей исправленной функцией формируется ошибка, которая позволяет посмотреть нашу ошибку.

4.5 Область видимости переменных в стеке вызовов В этом разделе мы анализируем область видимости переменных и как это может взаимодействовать с поведением функций. Поскольку этот момент может привести к неожиданным ошибкам в программе, то мы предупреждаем об его использовании в функциях, которые могут быть разработаны лучшим образом. Затем мы представляем два различных случая, где область видимости переменных использовалась без должного внимания и как это может привести к труднообнаружимым ошибкам в программе.

4.5.1 Обзор области видимости переменных В этом разделе мы представляем область видимости переменных и как это может влиять на поведение функций.

Предположим, что переменная, например a, определена в файле-сценарии и допустим, что та же переменная используется в функции, вызываемой прямо или косвенно. Что произойдёт в этом случае, зависит от чтения первой команды или написания переменной a в теле функции.

• Если первой прочитана переменная a, то значение этой переменной используется на более высоком уровне.

• Если первой написана переменная a, то местная переменная меняется (но переменная на более высоком уровне не меняется).

Представим эту общую схему работы на нескольких примерах.

В следующем примере мы представляем случай, когда переменная первой читается.

Следующая функция f вызывает функцию g и вычисляет выражение, зависящее от входного аргумента x и переменной a.

Заметим, что переменная a не является входным аргументом функции g. Заметим также, что в этом конкретном случае переменная a только читается, но не пишется.

В следующем примере мы определим переменные a и x. Затем мы вызовем функцию f с входным аргументом x. Когда мы определим значение a, то мы будет на уровне вызова №0 в стеке вызовов, в то время как в теле функции g мы находимся на уровне вызова №2 в стеке вызовов.

Мы видим, что функция f была вычислена правильно, используя в теле функции g, на уровне вызова №2, значение a, определённое на уровне вызова №0. Действительно, когда интерпретатор заходит в тело функции g, переменная a используется, но не определена.

Это из-за того, что она не является ни входным аргументом, ни локально определённой в теле функции g. Следовательно, интерпретатор ищет a на более высоких уровнях в стеке вызовов. В теле функции f, на уровне №1 нет переменной a. Следовательно, интерпретатор ищет на более высоком уровне. На уровне №0 интерпретатор находит переменную a, которая содержит [1 2 3]. Эта переменная используется для вычисления выражения y = a(1) + a(2)*x+a(3)*x2, что, наконец, позволяет вернуть переменную y с правильным содержанием. Этот процесс представлен на рисунке 25.

Рис. 25: Область видимости переменных. – Когда переменная используется, но не определена на нижнем уровне, то интерпретатор ищет ту же переменную на более высоком уровне стека вызовов. Будет использоваться первая переменная, которая будет найдена.

На практике, нам следует изменить построение функции g для того, чтобы сделать использование переменной a более ясным. Следующий пример определяет модифицированную версию функции g, где входной аргумент a делает явным тот факт, что мы используем переменную a.

endfunction Функция f, соответственно, должна быть обновлена для того, чтобы указать функции gfixed аргумент, который нужен. Это поясняет следующий пример.

Клиентский исходный код также следует обновить, как в следующем примере.

Функции ffixed и gfixed, с точки зрения разработки программного обеспечения, гораздо яснее, чем их предыдущие версии.

Теперь представим случай, где переменная a написана первой. Следующая функция f2 вызывает функцию g2 и вычисляет выражение, зависящее от входного аргумента x и переменной a.

Заметим, что переменная a написана прежде её использования.

Следующий пример использует те же данные, что и прежде.

Заметим, что значение, которое возвращено функцией f2, равно 38, а не предыдущему 17.

Это подтверждает, что использовалось значение локальной переменной a = [4 5 6], определённой на уровне №2, а не переменная a = [1 2 3], определённая на уровне №0. Более того, заметим что переменная a, определённая на уровне №0 не изменилась после вызова f2: её значение по-прежнему a = [1 2 3].

Стек вызовов может быть очень глубоким, но всегда будет то же поведение: какой бы ни был уровень в стеке вызовов, если переменная первой прочитана и была определена на более высоком уровне, то переменная будет использоваться напрямую. Это свойство кажется некоторым пользователям удобным. Мы подчёркиваем, что это может привести к программным ошибкам, которые невидимы и их трудно обнаружить. Действительно, разработчик функции g может непреднамеренно ошибочно использовать переменную a вместо другой переменной. Это приводит к проблемам, которые представлены в следующем разделе.

4.5.2 Плохая функция: неоднозначный случай В следующих разделах мы представляем случаи, где неверные использования области видимости переменных ведут к программным ошибкам, которые трудно анализировать.

В первом случае мы представляем две вложенные функции, которые производят неправильный результат, потому что функция на уровне №2 в стеке вызовов содержит ошибку и использует переменную, определённую на уровне №1. Во втором случае функция, использующая функцию обратного вызова, тихо рушится, поскольку она использует то же имя переменной, что и пользователь.

Неоднозначный случай представлен в следующем примере ошибки. Мы сначала определяем функцию f3, которая принимает x и a в качестве входных аргументов, но, неожиданно, не использует значение a. Вместо этого, выражение содержит переменную b.

В следующем примере мы определим значение a и x и вызовем функцию f3.

Значение, которое возвращает функция f3, соответствует параметрам, связанным с переменным b=[4 5 6]. Вместо этого переменная b, используемая в функции g3 была определена в функции f3, на более высоком уровне стека вызовов.

Очевидно, в этом примере функция g3 написана плохо и может содержать программную ошибку. Действительно, мы видим, что входной аргумент a никогда не используется, а b используется, но не является входным аргументов. Вся проблема в том, что, в зависимости от контекста, это может привести к ошибке, а может и не привести: мы на самом деле не знаем.

4.5.3 Плохая функция: случай тихого обрушения В этом разделе мы представляем типовое обрушение, вызванное неправильным использование области видимости переменных.

Следующая функция myalgorithm, предоставленная разработчиком, использует формулу производной первого прядка, основанную на прямой конечной разницы. Функция берёт текущую точку scivarx, функцию f и шаг h и возвращает приблизительную производную y.

Следующая функция myfunction, написанная пользователем, вычисляет многочлен второй степени по прямой формуле. Заметим, что вычисление выходного аргумента y использует точку x, которая является входным аргументом, и параметр a, который не является входным аргументом.

// От пользователя function y = myfunction ( x ) endfunction В следующем примере мы устанавливаем a, x, h и вызываем myalgorithm для того, чтобы вычислить численную производную. Мы сравниваем с точной производной и получаем хорошее соответствие.

-->y = myalgorithm ( x, myfunction, h ) Область видимости переменной a позволяет выполнять функцию myfunction. Действительно, переменная a определена пользователем на уровне №0 в стеке вызовов. Когда достигается тело функции myfunction, то интерпретатор находится на уровне №2, где переменная a используется, но не определена. Следовательно интерпретатор ищет переменную a на более высоком уровне стека вызовов. Нет такой переменной на уровне №1, поэтому используется переменная a, определённая на уровне №0, производящая, наконец, ожидаемый результат.

Фактически, этот способ использования области видимости переменных является опасной практикой программирования. На самом деле, пользователь делает предположение о внутреннем устройстве функции myalgorithm, и эти предположения могут быть ложными, ведущими к неверным результатам.

Мы слегка изменим функцию, предоставленную разработчиком, и переименуем шаг в a, вместо предыдущего h.

// От разработчика function y = myalgorithm2 ( x, f, a ) endfunction В следующем примере мы выполняем те же команды, что и прежде.

-->y = myalgorithm2 ( x, myfunction, h ) Неправильный индекс.

y = myalgorithm2 ( x, myfunction, h ) Ошибка Неправильный индекс является следствием того факта, что переменная a была переписана в теле функции myalgorithm2, на уровне №1 в стеке вызовов. Как и ранее, когда достигается тело функции myfunction на уровне №2 стека вызовов, то интерпретатор ищет переменную a, которая содержит шаг формулы конечной разности. Эта переменная a является матрицей значений типа double только с одной ячейкой. Когда интерпретатор пытается выполнить инструкцию a(1) + a(2)*x + a(3)*x2, это не получается, поскольку ни a(2) ни a(3) не существуют, то есть, целые числа 2 и 3 являются неверными значениями индексов переменной a.

Предыдущая ошибка была, фактически милой. Действительно, она предупреждает нас о том, что что-то неправильное произошло, так что мы можем изменить наш файл-сценарий.

В следующем случае набор команд пройдёт, но с неверным результатом, и это гораздо более опасная ситуация.

Мы снова изменим тело алгоритма, предоставленного разработчиком. В этот раз мы устанавливаем переменную a равной произвольной матрице значений типа double, как в следующей функции myalgorithm3.

// От разработчика function y = myalgorithm3 ( x, f, h ) endfunction Тело функции несколько странное, поскольку мы не используем переменную a. но функция остаётся правильной. На самом деле это представляет более сложные случаи, где реализация функции myalgorithm3 использует полный, по возможности широкий, набор переменных. В этом случае вероятность использования некоторых имён переменных в качестве пользователя гораздо выше.

В следующем примере мы вызываем функцию myalgorithm3 и сравниваем с ожидаемым результатом.

-->y = myalgorithm3 ( x, myfunction, h ) Мы видим, что этот набор команд не формирует ошибки. Мы также видим, что ожидаемый результат абсолютно неверен. Как и прежде, переменная a была переопределена на уровне №1, в теле функции myalgorithm3. Следовательно, когда выполняется выражение a(1) + a(2)*x + a(3)*x2, то используется значение [4 5 6] вместо матрицы, которую указал пользователь на более высоком уровне.

В нашем конкретном случае легко отладить проблему, поскольку мы имеем точную формулу для производной. На практике мы, вероятно, не будем иметь точную формулу (вот почему мы используем численные производные... ), так что будет гораздо труднее обнаружить и, в случае обнаружения, исправить проблему.

Использование переменной, определённой на более высоком уровне стека вызовов, может считаться плохой практикой программирования. В случае, когда разработчик функции этого не желал, это может вести к программным ошибкам, которые трудно обнаружить и может оказаться незамеченным долгое время, пока ошибки не исправят.

Используя этот способ, область видимости переменной рассматривается как плохая практика программирования. По-прежнему, может так случиться, что функция требует более одного входного аргумента, которые надо вычислить. Эта конкретная ошибка приводится в разделе 4.6.4, где мы представляем метод для указания дополнительных входных аргументов функции обратного вызова.

4.6 Проблемы с функциями обратного вызова В этом разделе, мы анализируем характерные проблемы функций обратного вызова.

Сначала мы рассмотрим взаимодействия между именами функций пользователя и разработчика. Мы представляем два различных типа проблем на специальных примерах. Затем мы представляем методы, которые позволяют частично решить эту проблему. Заключении раздела, мы представляем метод, который позволяет управлять функциями обратного вызова с дополнительными аргументами. Метод, который мы предлагаем, основан на списках, которые дают хорошую гибкость, поскольку список может содержать любой другой тип данных.

Мы подчёркиваем, что две первые проблемы, которые мы собираемся представлять в разделах 4.6.1 и 4.6.2 не являются программными ошибками интерпретатора. Как мы увидим, эти проблемы порождены областью видимости переменных, которая была представлена в предыдущем разделе. Вероятно, что эти проблемы останутся в будущих версиях интерпретатора. Следовательно, стоит проанализировать их детальнее, так чтобы мы могли понять и эффективно решать эти особые проблемы.

4.6.1 Бесконечная рекурсия В этом разделе мы представляем проблемы, которые порождают бесконечную рекурсию. Эта проблема случается, когда есть взаимодействие между именем функции, выбранным разработчиком и пользователем алгоритма. Поэтому наш анализ должен разделять точки зрения разработчика и пользователя.

Следующая функция myalgorithm, предоставленная разработчиком, берёт переменные x и f в качестве входных переменных и возвращает y. Предполагается, что переменная f является функцией и выражение y=f(x) вычисляется напрямую.

// На уровне разработчика Следующая функция myfunction, предоставленная пользователем, берёт в качестве входного аргумента x и возвращает y. Для того, чтобы вычислить y, пользователь применяет вторую функцию f, которую он написал для этого.

// На уровне пользователя В следующем примере пользователь устанавливает переменную x и вызывает функцию myalgorithm со входными аргументами x и myfunction.

-->y = myalgorithm ( x, myfunction ) Слишком сложная рекурсия! (заполнена таблица рекурсии) Эта рекурсия, которая в теории бесконечна, фактически конечна, поскольку Scilab разрешает только ограниченное количество вызовов f.

Причина этой ошибки заключается в конфликте имён функций разработчика и пользователя, которые в обоих случаях используют имя переменной f. С точки зрения пользователя, функция myfunction просто вызывает f. Но интерпретатор не видит подобного файла-сценария. С точки зрения интерпретатора, символы myfunction и f являются переменными, которые хранятся во внутренних структурах данных интерпретатора. Переменная myfunction имеет особый тип данных: она является функцией. То же самое справедливо для пользовательской f. Обе этих переменных определены на уровне №0 в стеке вызовов.

Следовательно, перед вызовом myalgorithm, на уровне №0, функция f определяется пользователем, с телом "y = x(1)2 + x(2)2". Когда вызывается myalgorithm, которую мы вводим на уровень №1 и переменная f рассматривается в качестве входного аргумента:

её содержимое f=myfunction. Следовательно, функция f, которая ранее определена пользователем, была переписана и потеряла своё первоначальное значение. Интерпретатор выполняет выражение y = f ( x ), которое вызывает обратно myfunction. В теле функции myfunction, на уровне №2, интерпретатор находит выражение y = f ( x ). Затем интерпретатор ищет переменную f на текущем уровне в стеке вызовов. Там нет такой переменной.

Следовательно интерпретатор ищет переменную f на более высоком уровне в стеке вызовов.

На уровне №1 переменная f определена с содержанием f=myfunction. Это значение, следовательно, используется на уровне №2. Интерпретатор выполняет выражение y = myfunction ( x ) и выходит на уровень №3. Затем интерпретатор ищет переменную f на текущем уровне.

На этом уровне нет такой переменной. Как и прежде, интерпретатор ищет переменную f на более высоком уровне стека вызовов. И снова интерпретатор вызывает функцию f, которая является фактически самой функцией myfunction. Этот процесс повторяется снова и снова до тех пор, пока интерпретатор не достигнет максимального разрешённого размера стека вызовов и сформируется ошибка. Следующий пример показывает последовательность событий.

-->y = myalgorithm ( x, myfunction ) Подчеркнём, что проблема является следствием области видимости переменных в языке Scilab’а. Это напрямую означает следующие возможности языка.

• Функция хранится как любая другая переменная (но с особым типом данных).

• Если переменная неизвестна на уровне №k, то её ищут на более высоком уровне в стеке вызовов до тех пор, пока её или не найдут или не будет сформирована ошибка (а именно Неизвестная переменная ).

Заметим, что это не значит, что пользователь знает, что разработчик использовал имя переменной f. Обратное так же верно для разработчика, который не может предугадать, что пользователь будет использовать переменную f. Заметим, что нет простого способа для пользователя изменить эту ситуацию. Функция myalgorithm может быть предоставлена внешним модулем. В этом случае смена имени переменной, используемой в функции разработчика может быть невозможным для пользователя. Как мы увидим позднее в этом разделе, пользователь по-прежнему может найти решение на основе небольшой реорганизации исходного кода.

4.6.2 Неправильный индекс В этом разделе мы представляем ситуацию, где интерпретатор формирует ошибку Неправильный индекс из-за конфликта между именами функций пользователя и разработчика.

В следующей функции myalgorithm, предоставленной разработчиком, мы устанавливаем локальную переменную f в 1. Затем мы вызываем обратно функцию userf с входным аргументом x и возвращаем выходной аргумент y.

// На уровне разработчика function y = myalgorithm ( x, userf ) Заметим, что переменная f установлена, но не используется. Функция по-прежнему годная, и это не меняет наш анализ.

Следующая функция myfunction, предоставленная пользователем, вызывает функцию f с входным аргументом x и возвращает выходной аргумент y.

// На уровне пользователя В следующем примере мы устанавливаем переменную x и вызываем функцию myalgorithm. Это формирует ошибку Неправильный индекс в теле функции myfunction.

-->y = myalgorithm ( x, myfunction ) Неправильный индекс.

y = myalgorithm ( x, myfunction ) Объяснение следующее. На уровне №0 в стеке вызова, функция f определена так, как ожидает пользователь. В теле функции myalgorithm мы находимся на уровне №1 в стеке вызовов. Переменная f обновлена командой f=1: теперь f является матрицей значений типа double. Затем интерпретатор выполняет выражение y = userf(x). Поскольку userf это myfunction, то интерпретатор вызывает функцию myfunction и входит на уровень № в стеке вызовов. На этом уровне интерпретатор выполняет выражение y = f ( x ). Затем интерпретатор ищет переменную f на уровне №2: нет такой переменной на этом уровне.

Затем интерпретатор ищет переменную f на более высоком уровне. На уровне №1 интерпретатор находит переменную f, которая является матрицей значений типа double. В этом контексте выражение y = f ( x ) не имеет смысла: переменная x интерпретируется как индекс матрицы значений типа double f. Поскольку x=[1 2], то интерпретатор ищет ячейки с индексами 1 и 2 в f. Но f содержит только матрицу значений типа double размером 1 1.

Следовательно есть только одна ячейка в f, поэтому формируется ошибка Неправильный индекс.

Как и в предыдущем разделе, пользователь не может предугадать имена переменных, выбранных разработчиком, а обратное верно для разработчика.

В следующем разделе мы предлагаем методы решения этих проблем функций обратного вызова.

4.6.3 Решения В этом разделе мы представляем решения для проблем функций обратного вызова, которые мы представили ранее. Решение проблем может быть сделано двумя разными способами.

• Изменить функцию разработчика так, чтобы было меньше шансов получить конфликт с именами пользовательских переменных.

• Изменить функцию пользователя так, чтобы он мог использовать функцию разработчика.

Очевидно, что мы можем также использовать и тот и другой. В общем, это задача разработчика предоставлять функции, которые достаточно хорошо разработаны, так что пользователь не должен озадачиваться странными программными ошибками. Поэтому мы представляем это обновление первым. Обновление пользовательской функции представлено во второй часть этого раздела.

Следующая модифицированная функция представляет метод, который разработчик может использовать для написания функции myalgorithm. Мы можем видеть, что второй аргумент myalgorithm_f имеет длинное и сложное имя. Это уменьшает вероятность получения конфликтов между именами переменных пользователя и разработчика.

function y = myalgorithm ( x, myalg orithm_f ) Это обходной манёвр: по-прежнему есть вероятность, что пользователь получит ошибку.

Действительно, если пользователь выберет имя переменной myalgorithm_f, то останется конфликт между именами переменных пользователя и разработчика. Но это весьма маловероятно.

Следующая модифицированная функция представляет метод, который пользователь может использовать для написания функции myfunction. Вместо определения функции f вне тела функции myfunction, мы определяем её внутри. В новой версии переменная f на этот раз определена локально, внутри myfunction. Это ограничивает видимость локальной функции f, к которой может получить доступ myfunction (и более низкие уровни), и она больше не появляется в глобальной области видимости. Следовательно, когда мы вызываем f, то интерпретатор не ищет на более высоких уровнях стека вызовов: переменная f определена на текущем уровне.

В следующем примере мы вызываем функцию myalgorithm с обновлённой функцией myfunction.

-->y = myalgorithm ( x, myfunction ) Как мы можем видеть, теперь проблема решена и мы получаем ожидаемый результат.

4.6.4 Функции обратного вызова с дополнительными аргументами В этом разделе мы представляем метод, который позволяет решить проблемы, связанные с функциями обратного вызова с дополнительными аргументами. Метод, который мы предлагаем основан на списках, которые предоставляют хорошую гибкость, поскольку они могут содержать любой другой тип данных.

Когда мы рассматриваем алгоритм, который берёт функцию обратного вызова в качестве входного аргумента, то заголовок функции, которая вызывается обратно, вообще определённо фиксирован, по выбору разработчика алгоритма. Но пользователь алгоритма может захотеть указать функцию, которая не точно совпадает с требуемым заголовком.

Например, алгоритм может ожидать функцию с заголовком y=f(x), а пользователь имеет функцию, которая требует дополнительный параметр a. Для того, чтобы избежать эту путаницу, созданную использованием области видимости переменных, пользователь может выбрать обновление заголовка функции так, чтобы переменная a была теперь входным аргументом. Это ведёт к заголовку y=g(x,a). В этом случае мы предполагаем, что дополнительный аргумент a является константой, что значит, что алгоритм не изменяет её содержимое.

Простое указание функции g не может работать, поскольку заголовок не совпадает. Для того, чтобы проиллюстрировать эту ситуацию, мы анализируем случай, когда мы хотим вычислить числовую производную.

Следующая функция myderivative1 использует прямую формулу конечной разности для вычисления числовой производной.

function fp = myderivative1 ( _ _m yde ri va tiv e_ f_ _, x, h ) Мы рассмотрим функцию myfun, которая вычисляет косинус многочлена второй степени.

В следующем примере мы сравниваем числовую производную с точной производной.

--> expected = - sin (1+2* x +3* x ^2) * (2+6* x ) Поскольку два значения хорошо совпадают, то мы теперь уверены в нашей реализации.

Но было бы яснее, если параметры многочлена были сохранены в матрице значений типа double. Это приводит к следующей функции myfun2, которая берёт точку x и параметр a в качестве входных аргументов.

Для того, чтобы управлять данной ситуацией, мы модифицируем реализацию алгоритма конечной разности и создадим функцию myderivative2, которая будет подробно рассмотрена позже в этом разделе. В следующем примере мы вызываем myderivative2 и указываем список list(myfun2,a) в качестве первого аргумента. Функция myderivative2 предполагает, что первый элемент списка это имя функции, и что остальные элементы списка являются дополнительными аргументами функции, которая будет выполнена. Здесь единственный дополнительный элемент это a.

--> fp = myderivative2 ( list ( myfun2, a ), x, h ) --> expected = - sin ( a (1)+ a (2)* x + a (3)* x ^2) * ( a (2)+2* a (3)* x ) Следующая функция myderivative2 даёт гибкую реализацию числовой производной, которая позволяет различать дополнительные аргументы в заголовке функции.

1 function fp = myderivative2 ( _ _m yde ri va tiv e_ f_ _, x, h ) 3 if ( and ( tyfun < >[ " list " " function " " fptr " ]) ) then 4 error ( msprintf ( " %s : Unknown function type : %s ",..

16 error ( msprintf ( " %s : Unknown function type : %s ",..

26 endfunction В строке №2 мы выясняем тип входного аргумента myderivative_f. Если этот аргумент не является ни списком ни указателем функции (т. е. примитивом), то мы формируем ошибку. Тогда код рассматривает два случая. Если первый аргумент является списком, то мы проверяем в строках с №8 по №12 число пунктов списка. Затем, в строке №13, мы сохраним первый элемент в отдельной переменной, которая предполагается является функцией.

В строках с №14 по №18 мы проверяем тип этой переменной и формируем ошибку, если переменная не является функцией. Затем в строках №19 и №20 мы вычисляем два значения функции fxph и fx. Заметьте, что мы используем выражение myderivative_f(2:$) для того, чтобы предоставить дополнительные аргументы вызывающей функции. Из-за особого способа, которым элементы выделяются из списка, это создаёт ряд входных аргументов, которые требуются заголовком функции, которую вызвали.

На практике метод, который мы уже представили чрезвычайно гибок. Функция может предоставить более одного дополнительного аргумента. Фактически, число дополнительных аргументов может быть уменьшено использованием сбора списка всех необходимых параметров. Действительно, списки могут быть вложены, что позволяет собрать требуемые параметры в единственную дополнительную переменную.

4.7 Мета-программирование: execstr и deff В этом разделе мы представляем функции, которые позволяют выполнить инструкции, которые определены как строки. Поскольку эти строки могут формироваться динамически, мы можем сделать программы, которые имеют уровень гибкости, который не может быть достигнут другими средствами. В первой части мы представляем функцию execstr и даём пример использования этой чрезвычайно мощной функции. Во второй части мы представляем функцию deff, которая позволяет динамически создавать функции, основанные на двух строках, содержащих заголовок и тело функции. В заключительной части мы представляем практическое использование функции execstr, где мы объединяем два модуля, которые не могут модифицироваться пользователем и которые не совпадают точно.

Рисунок 26 представляет функции, которые позволяют динамически выполнять инструкции, основанные на строках.

4.7.1 Основное использование execstr В этом разделе мы представляем функцию execstr.

Функция execstr берёт в качестве своего первого входного аргумента строку, которая содержит правильную инструкцию Scilab’а. Затем функция выполняет инструкцию как если бы это было частью оригинального файла-сценария на том же самом уровне стека вызовов.

В следующем примере мы динамически формируем строку, содержащую инструкцию, которая отображает строку "foo". Сначала мы определяем переменную s, которая содержит строку "foo". Затем мы устанавливаем переменную instr, конкатенируя переменную s со строками disp(" и "). Это выполняется оператором +, который позволяет конкатенировать свои операнды. Двойные кавычки " дублируются, что делает так, что выражение интерпретируется как символ " внутри целевой строки. Действительно, если эти кавычки не появятся дважды, то это будет интерпретироваться как конец строки: дублирование кавычек позволяет избежать знака кавычек. Наконец, мы запустим функцию execstr, которая распечатывает "foo".

--> execstr ( instr ) Аргумент execstr может содержать любую правильную инструкцию Scilab’а. Например, мы можем динамически установить новую переменную с динамически созданным содержанием. В следующем примере мы создаём строку instr, содержащую инструкцию z=1+2.

Затем мы выполняем это выражение и проверяем, что переменная z действительно установлена равной 3.

--> execstr ( instr ) В качестве другого примера использования execstr мы можем читать коллекцию файлов данных. Предположим, что файл datafile1.txt содержит строки и файл datafile2.txt содержит строки Следующий пример позволяет читать эти два файла последовательно. Функция read берёт строку, представляющую файл, содержащий матрицу элементов типа double и количество строк и столбцов в матрице. Набор команд устанавливает переменную data, которая содержит список матрицы, содержащей данные, прочитанные в двух файлах.

Следующий пример показывает динамику предыдущего примера.

4.7.2 Основное использование deff Функция deff позволяет динамически определять функцию, из двух строк, содержащих заголовок и тело функции.

В следующем примере мы определим функцию myfun, которая принимает матрицу значений типа double x в качестве входного аргумента и возвращает выходной аргумент y.

Сначала мы определяем заголовок функции в виде строки "y=myfun(x)" и устанавливаем переменную header. Затем мы определяем тело функции и, наконец, мы вызываем функцию deff, которая создаёт требуемую функцию.

Следующий пример показывает, что новая функция myfun может быть использована как любая другая функция.

Единственная разница заключается в типе функции, созданной с помощью deff, как представлено в разделе 4.1.1. В следующем примере мы показываем, что функция, созданная с помощью deff, имеет тип 13, который соответствует компилированному макросу.

Если мы добавим аргумент "n" к вызову функции deff, то это скажет интерпретатору создать вместо этого некомпилированный макрос, который производит функцию с типом 11.

Warning : redefining function : myfun.

Use funcprot (0) to avoid this message Мы подчёркиваем, что для того, чтобы создать новую функцию, могла бы использоваться функция execstr вместо deff. Например, следующий набор команд показывает способ определения новой функции с помощью execstr отдельным определением заголовка, тела и окончания функции. Эти строки затем конкатенируются в единую инструкцию, которая, наконец, исполняется для того чтобы определить новую функцию.

4.7.3 Практический пример оптимизации В этом разделе мы представляем практический случай использования методов, которые мы представили в предыдущих разделах.

Этом примере мы используем функцию optim, которая является программой для поиска решения числовой оптимизации. Мы представляем ситуацию, когда нам нужна функция для определения функции адаптера (т. е. клей ), которая соединяет optim с модулем, предоставляющим набор задач оптимизации. В первом методе, мы определяем функцию адаптера и передаём функцию обратного вызова с дополнительными аргументами в функцию optim. Во втором методе, мы используем функцию execstr для создания функции, чьё тело формируется динамически.

Пример, который мы выбрали, может показаться сложным. Хотя на практике он представляет практическую задачу разработки программного обеспечения, где могут использоваться только нетривиальные решения.

Предположим, что нас интересует расчёт характеристик функции нелинейной оптимизации optim, предоставляемой Scilab. В этом случае, мы можем использовать модуль Atoms uncprb, который предоставляет коллекцию из 23 тестовых задач неограниченной оптимизации, которая известна как коллекция Морэ, Гарбоу и Хильстрома (More, Garbow, Hillstrom).

Для того, чтобы установить этот модуль, мы используем следующую инструкцию:

и перезапускаем Scilab.

Мы подчёркиваем, что мы рассматриваем здесь точку зрения пользователя, который не может модифицировать ни один из этих пакетов, то есть, не может модифицировать ни функцию optim, ни модуль uncprb. Как мы вскоре увидим, нет точного совпадения между заголовком целевой функции, которая требуется для функции optim и заголовок тестовых функций. которые предоставлены модулем uncprb. Точнее, число и тип входных и выходных аргументов, которые требуются для функции optim, не совпадают с числом и типом входных и выходных аргументов, которые предоставляются модулем uncprb. Это не из-за плохого устройства обоих инструментов: фактически невозможно предоставить универсальный заголовок, удовлетворяющий всевозможным нуждам. Следовательно, мы должны создать промежуточную функцию, которая делает клей между двумя этими компонентами.

Модуль uncprb предоставляет значение функции, градиент и вектор функции и якобиан для всех тестовых задач, и предоставляет матрицу Гессе для 18 задач. Точнее, этот модуль предоставляет следующие функции, где nprob номер задачи от 1 до 23.

[n,m, x0 ]= uncprb_getinitf ( nprob ) f = uncprb_getobjfcn (n,m,x, nprob ) g = uncprb_getgrdfcn (n,m,x, nprob ) Функция uncprb_getinitf возвращает размер n задачи, число m функции и начальное предположение x0. Действительно, существующая целевая функция является суммой квадратов m функций. Функция uncprb_getobjfcn возвращает значение целевой функции, при этом функция uncprb_getgrdfcn возвращает градиент.

Функция optim является программой поиска решения нелинейной неограниченной оптимизации, предоставляющей несколько алгоритмов для этого класса задач. Она может управлять неограниченными или частично ограниченными задачами. Самая простая инструкция вызова функции optim:

где costf функция (т. е. целевая) цены, которую нужно минимизировать, x0 начальная точка (т. е. начальное предположение), fopt значение минимума функции, а xopt точка, которая достигает это значение. Функция цены costf должна иметь заголовок где x текущая точка, ind целое число с плавающей запятой, представляющее что должно быть вычислено, f значение функции, g градиент. На выходе ind это флаг, посылаемый функцией программе поиска решения оптимизации, например чтобы прервать алгоритм.

Мы должны сначала получить параметры задачи, так, чтобы мы могли установить клей-функцию. В следующем примере мы получаем параметры первой задачи оптимизации.

[n,m, x0 ] = uncprb_getinitf ( nprob );

Вопрос в том как склеить эти два компонента, так чтобы optim могла использовать uncprb? Очевидно, что мы не можем передать ни uncprb_getobjfcn ни uncprb_getgrdfcn в качестве входных аргументов в optim, поскольку целевая функция должна вычислить и значение функции и градиент. Решение этой задачи заключается в определении промежуточной функции, которая имеет заголовок, требуемый функцией optim, и который вызывает функции scifununcprb_getobjfcn и uncprb_getgrdfcn. Единственной проблемой является управление nprob, n и m, которое зависит от задачи, которую нужно решить. Чтобы это сделать, возможны два разных способа.

• Мы можем определить функцию с дополнительными аргументами nprob, n и m и использовать специальные возможности optim для управления ими.

• Мы можем определить функцию, которая динамически определяется с помощью так, что в конце переменные nprob, n и m являются константами.

Далее мы изучим оба метода.

Первый метод основан на управлении дополнительными аргументами функции обратного вызова, которые были представлены в разделе 4.6.4. Определим целевую функцию costfun, которая имеет ожидаемую последовательность вызова, с дополнительными аргументами nprob, n и m.

function [f,g, ind ]= costfun (x, ind, nprob,n, m ) // Делаем вектор-столбец f = uncprb_getobjfcn (n,m,x, nprob ) g = uncprb_getgrdfcn (n,m,x, nprob ) endfunction Затем, мы можем использовать специальные возможности функции optim, которые управляют случаем, в котором для целевой функции нужны дополнительные входные аргументы.

Аргумент costf может также быть списком (myfun,a1,a2,...). В этом случае myfun, первый элемент списка, должна быть функцией и должна иметь заголовок:

Мы используем эту возможность в следующем файле-сценарии:

[n,m, x0 ] = uncprb_getinitf ( nprob );

myobjfun = list ( costfun, nprob,n, m );

[ fopt, xopt ]= optim ( myobjfun, x0 ) Предыдущий файл-сценарий производит следующий вывод:

- - >[ fopt, xopt ]= optim ( myobjfun, x0 ) Мы знаем, что глобальный минимум этой задачи равен x = (1, 1), так что предыдущий результат верен.

Второй метод основан на динамическом создании целевой функции с помощью функции execstr.

В следующем примере мы определяем функцию objfun, которая будет передана в качестве входного аргумента функции optim. Тело функции objfun динамически определяется использованием переменных nprob, m и n, которые были определены ранее. Заголовок, тело и окончание функции затем конкатенируются в матрицу строковых элементов instr.

header = " function [ fout, gout, ind ]= objfun (x, ind ) " ;

" fout = uncprb_getobjfcn ( " + string ( n )+ "," + string ( m )+ ",x, " + string ( nprob )+ " ) " " gout = uncprb_getgrdfcn ( " + string ( n )+ "," + string ( m )+ ",x, " + string ( nprob )+ " ) " footer = " endfunction " instr = [ header body footer Предыдущий исходный код довольно абстрактный. Фактически генерируемая функция простая, как показана в следующем примере.

Как мы можем видеть, входной аргумент x функции objfun единственный оставшийся:

остальные параметры заменены их действительными значениями для этой конкретной задачи. Следовательно, функция objfun явно определена и не использует своё окружение для получения, например, значения nprob. Мы подчёркиваем этот особый момент, который показывает, что использование области видимости переменных через стек вызовов, как представлено в разделе 4.5.1, может быть исключено использованием функции execstr.

Для того, чтобы определить целевую функцию, мы, наконец, используем execstr.

В следующем примере мы вызываем функцию optim и вычисляем решение первой тестовой задачи.



Pages:     | 1 || 3 | 4 |


Похожие работы:

«Пояснительная записка. Рабочая программа разработана на основании: 1. Закона Об образовании 2. Программы специальных (коррекционных) образовательных учреждения VIII вида под ред. В.В.Воронковой 5классы: Сб. 1. - М.: Гуманит.изд.центр ВЛАДОС, 2000г. и допущена Министерством образования и науки РФ. 3. Учебного плана образовательного учреждения Согласно действующему Базисному учебному плану рабочая программа для 5-го класса предусматривает обучение природоведению в объеме 2 часов в неделю....»

«Паспорт Программы инновационного на период 2012-2017гг. развития ОАО Зарубежнефть (с перспективой до 2030 г.) Паспорт Программы инновационного развития ОАО Зарубежнефть на период 2012-2017 гг. (с перспективой до 2030 г.) Москва 2013 Паспорт Программы инновационного развития на период 2012-2017гг. ОАО Зарубежнефть (с перспективой до 2030 г.) Раздел 1. Основные направления научно-технологического развития 3 Раздел 2. Важнейшие мероприятия по инновационному развитию 6 Раздел 3. Кадровое...»

«Негосударственное образовательное учреждение высшего профессионального образования Международный институт экономики и права Кафедра менеджмента и маркетинга ПРОГРАММА ПРАКТИКИ ПЕДАГОГИЧЕСКАЯ ПРАКТИКА Направление подготовки 080200.68 – Менеджмент (код и наименование направления подготовки) Наименование магистерской программы Управление проектами (наименование магистерской программы) Степень выпускника Магистр Форма обучения _очная _ Москва 2014 Авторы-составители: канд. экон. наук, проф. Е.Н....»

«ПРИОРИТЕТНЫЙ НАЦИОНАЛЬНЫЙ ПРОЕКТ ОБРАЗОВАНИЕ ПОДДЕРЖКА ВУЗОВ, ВНЕДРЯЮЩИХ ИННОВАЦИОННЫЕ ОБРАЗОВАТЕЛЬНЫЕ ПРОГРАММЫ ФЕДЕРАЛЬНОЕ АГЕНТСТВО ПО ОБРАЗОВАНИЮ Государственное образовательное учреждение высшего и профессионального образования РОССИЙСКИЙ УНИВЕРСИТЕТ ДРУЖБЫ НАРОДОВ (РУДН) ОТЧЕТ ПО РЕЗУЛЬТАТАМ РЕАЛИЗАЦИИ в 2007 г. ИОП: Создание комплекса инновационных образовательных программ и формирование инновационной образовательной среды, позволяющих эффективно реализовывать государственные интересы РФ...»

«Министерство образования и наук и РФ Федеральное государственное бюджетное образовательное учреждение высшего профессионального образования Петрозаводский государственный университет Кольский филиал ПРОГРАММА XV Межрегиональной научно-практической конференции с международным участием 17-19 апреля 2012 года Апатиты 2012 Состав Организационного комитета по подготовке и проведению XV Межрегиональной научно-практической конференции с международным участием Бюро Организационного комитета Маслобоев...»

«Институт стратегического управления социальными системами Бойцов А.А. www.ismss.ru Создание Северо-Западного регионального центра КАМАЗ Бизнес-план Создание Северо-Западного регионального центра КАМАЗ 1 Бизнес-план Содержание Резюме бизнес-плана 1. Характеристика региона 1.1. Краткая характеристика региона (Санкт-Петербург) 1.1.1. Общие данные 1.1.2. Отраслевая структура промышленного производства СПб 1.1.3. Динамика изменения валового регионального продукта за 10 лет 1.1.4. Тенденции развития...»

«БЕЛОРУССКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ ФИЛОЛОГИЧЕСКИЙ ФАКУЛЬТЕТ  УТВЕРЖДАЮ  Проректор по учебной работе _ / А.В. Данильченко             (подпись)                                                                                        (И.О.Ф.)      25апреля 2013          (дата утверждения)            Регистрационный № УД 9041 /уч.    ПРОГРАММА ВСТУПИТЕЛЬНОГО ИСПЫТАНИЯ В МАГИСТРАТУРУ ПО СПЕЦИАЛЬНОСТИ 1-21 80 04 РОМАНСКИЕ ЯЗЫКИ         Минск ПОЯСНИТЕЛЬНАЯ ЗАПИСКА     ...»

«ФЕДЕРАЛЬНОЕ АГЕНТСТВО ПО ОБРАЗОВАНИЮ Государственное образовательное учреждение высшего профессионального образования ШУЙСКИЙ ГОСУДАРСТВЕННЫЙ ПЕДАГОГИЧЕСКИЙ УНИВЕРСИТЕТ Кафедра теоретических основ физического воспитания УТВЕРЖДЕН постановлением ученого совета протокол № 6 от 26.05.2009 Председатель совета, ректор И.Ю. Добродеева УЧЕБНО-МЕТОДИЧЕСКИЙ КОМПЛЕКС Для дисциплины АНАТОМИЯ для специальности 050720.65 Физическая культура со специализацией Физическое воспитание в дошкольных учреждениях...»

«РОССИЙСКАЯ АКАДЕМИЯ НАУК Федеральное государственное бюджетное учреждение науки ИНСТИТУТ КРИОСФЕРЫ ЗЕМЛИ СИБИРСКОГО ОТДЕЛЕНИЯ РАН Программа принята УТВЕРЖДАЮ Ученым советом Института Директор ИКЗ СО РАН _ 2012 года В.П. Мельников (протокол №_) “_” 2012 г. ПРОГРАММА ВСТУПИТЕЛЬНОГО ЭКЗАМЕНА В АСПИРАНТУРУ по специальности 25.00.10 Геофизика. Геофизические методы поисков месторождений полезных ископаемых отрасли наук 25.00.00. Науки о Земле ТЮМЕНЬ Данная программа содержит перечень вопросов для...»

«МИНИСТЕРСТВО ОБРАЗОВАНИЯ И НАУКИ УКРАИНЫ ДОНЕЦКИЙ НАЦИОНАЛЬНЫЙ ТЕХНИЧЕСКИЙ УНИВЕРСИТЕТ Кафедра философии Реферат по дисциплине Философия науки на тему: Эволюция науки. Классика – неклассика – постнеклассика Выполнил ст. гр. ИУС-11м Зверьков П.С. Проверил Алексеева Л.А. Донецк 2012 2 Введение В реферате планируется рассмотреть процесс эволюции науки на трёх основных этапах ( классификация В.С. Степина) – классическая наука ( XVII – XIX вв), неклассическая наука (конец XIX – первая половина XX...»

«Тюльпан с глубокой древности являлся для наших предков “божественным цветком” – символом оживающей природы. По мнению археолога М.Е.Массона и искусствоведа Г.А.Пугаченковой, в Средней Азии, в том числе в Туркменистане, уже в эпоху античности существовал праздник тюльпана, связанный с наступлением весны. В архитектурном декоре Туркменистана, да и вообще Средней Азии и Ирана, стилизованные виноградные листья и лотосовидный тюльпан в V-VII вв.н.э. занимали господствующее положение. Даже в более...»

«Федеральное агентство связи Федеральное государственное образовательное бюджетное учреждение высшего профессионального образования Московский технический университет связи и информатики Утверждена решением Ученого совета МТУСИ протокол № 11 от 01 07 2011 г. Председатель Ученого совета А.С. Аджемов ОСНОВНАЯ ОБРАЗОВАТЕЛЬНАЯ ПРОГРАММА ВЫСШЕГО ПРОФЕССИОНАЛЬНОГО ОБРАЗОВАНИЯ Направление подготовки 210700 - Инфокоммуникационные технологии и системы связи Квалификация (степень) Бакалавр Форма...»

«Государственное образовательное учреждение высшего профессионального образования Липецкий государственный технический университет Инженерно-строительный факультет УТВЕРЖДАЮ Декан ИСФ Бабкин В.И. _ 2011 г. РАБОЧАЯ ПРОГРАММА ДИСЦИПЛИНЫ Основы архитектуры и строительных конструкций 270800.62 Строительство Направление подготовки Профиль подготовки Проектирование зданий Квалификация (степень) выпускника бакалавр Нормативный срок обучения 4 года Форма обучения очная г. Липецк – 2011 г. 1. Цели и...»

«КРАЕВОЕ ГОСУДАРСТВЕННОЕ БЮДЖЕТНОЕ ОБРАЗОВАТЕЛЬНОЕ УЧРЕЖДЕНИЕ НАЧАЛЬНОГО ПРОФЕССИОНАЛЬНОГО ОБРАЗОВАНИЯ ПРОФЕССИОНАЛЬНОЕ УЧИЛИЩЕ № 79 П. КОШУРНИКОВО УТВЕРЖДАЮ: Зам. директора по УПР _И.Ф. Копнина _20г. РАБОЧАЯ ПРОГРАММА УЧЕБНОЙ ДИСЦИПЛИНЫ ОП.03. Техническое оснащение и организация рабочего места Профессия 260807.01 Повар, кондитер Нормативный срок обучения – 2 года и 5 мес. на базе основного общего образования 1 Рабочая программа учебной дисциплины разработана на основе Федерального...»

«Муниципальное автономное общеобразовательное учреждение средняя общеобразовательная школа № 5 г. Курганинск Муниципальное автономное учреждение дополнительного образования Центр детского творчества детей г. Курганинск Силантьев А.Н., Силантьев М.Н., Южанина И.М., Шульга А.А. Авторская образовательная программа Организация живых систем Рассчитана на обучающихся 13-17 лет Срок реализации 1 год Пояснительная записка Биологические науки изучают многообразие строения и функций живых организмов, их...»

«ПРОГРАММА вступительного экзамена в аспирантуру ОАО Концерн ЦНИИ Электроприбор по специальности 05.11.14 Технология приборостроения Санкт-Петербург 2 1. Технологический процесс и его содержание Производственный и технологический процессы и их элементы. Характерные особенности изготовления деталей, сборки и регулировки приборов, обусловленные основными требованиями к приборам. Влияние типа производства на построение технологического процесса. 2. Единая система технологической подготовки...»

«Новые возможности SOLIDWORKS 2011 Содержание Новые возможности: SolidWorks 2011 Уведомления 1 Интерфейс пользователя Поиск SolidWorks Кнопки Сохранить как и Свойства файла на панели инструментов Стандартная Пиктограммы PropertyManager Отчеты об ошибках 2 Основные принципы SolidWorks Интерфейс программирования приложений Копировать проект Документация Новые учебные пособия Администрация Toolbox - Обзор 3 3D ContentCentral Инструмент Defeature Configuration Publisher (Издатель конфигураций) 3D...»

«ГУП Бизнес-инкубатор Курганской области ОБ ИТОГАХ ДЕЯТЕЛЬНОСТИ ЗА 2013 год И ЗАДАЧАХ НА 2014 год Докладчик: Юсупов Артур Марсельевич Руководитель ГУП Бизнес-инкубатор Курганской области ГУП Бизнес-инкубатор Курганской области Организация ИНФРАСТРУКТУРЫ ПОДДЕРЖКИ малого и среднего бизнеса Курганской области Создан и начал функционировать с 19 мая 2009 года Взаимодействие и организация работы 25 ИНФОРМАЦИОННО-КОНСУЛЬТАЦИОННЫХ ЦЕНТРОВ в муниципальных образованиях Курганской области Координация и...»

«ФЕДЕРАЛЬНОЕ АГЕНТСТВО ПО РЫБОЛОВСТВУ Федеральное государственное бюджетное образовательное учреждение высшего профессионального образования Калининградский государственный технический университет (ФГБОУ ВПО КГТУ) УТВЕРЖДАЮ Проректор по НР _А.В. Иванов 2012 г. ПРОГРАММА вступительного экзамена в аспирантуру по специальности 05.18.04 Технология мясных, молочных, рыбных продуктов и холодильных производств Присуждаемая ученая степень кандидат технических наук Калининград Настоящая программа...»

«1. ПОЯСНИТЕЛЬНАЯ ЗАПИСКА 1.1. Краткая характеристика дисциплины Рабочая программа по дисциплине Трудовое право разработана на основе Государственного образовательного стандарта, в рамках Основной образовательной программы, с учетом особенностей профессионального обучения в Академии социального образования (г. Казань). Программа знакомит преподавателей, аспирантов и студентов с целями и задачами означенной дисциплины Трудовое право и тематическим планом часов. Программа включает планы...»






 
2014 www.av.disus.ru - «Бесплатная электронная библиотека - Авторефераты, Диссертации, Монографии, Программы»

Материалы этого сайта размещены для ознакомления, все права принадлежат их авторам.
Если Вы не согласны с тем, что Ваш материал размещён на этом сайте, пожалуйста, напишите нам, мы в течении 1-2 рабочих дней удалим его.