Главная страница

Программирование в Scilab Микаэль Боден Micha


Скачать 1.35 Mb.
НазваниеПрограммирование в Scilab Микаэль Боден Micha
Дата18.09.2022
Размер1.35 Mb.
Формат файлаpdf
Имя файлаprogscilab-v.0.10_ru.pdf
ТипРеферат
#683094
страница5 из 13
1   2   3   4   5   6   7   8   9   ...   13
«Ringo».
- - > s e t f i e l d (2 , " R i n g o " , p )
- - > p p
=
p (1)
! p e r s o n f i r s t n a m e n a m e b i r t h y e a r
!
p (2)
R i n g o p (3)
S m i t h p (4)
1 9 9 7 .
Может так случиться, что мы знаем значения полей во время создания типизирован- ного списка. Мы можем добавить эти значения ко входным аргументам функции tlist в строгом порядке. В следующем примере мы определим person p и установим значения полей во время создания типизированного списка.
- - > p = t l i s t ( ..
- - >
[ " p e r s o n " , " f i r s t n a m e " , " n am e " , " b i r t h y e a r " ] , ..
- - >
" P au l " , ..
- - >
" S m i t h " , ..
- - >
1 9 9 7 )
p
=
p (1)
! p e r s o n f i r s t n a m e n a m e b i r t h y e a r
!
p (2)
P a u l p (3)
S m i t h p (4)
1 9 9 7 .
Интересной чертой типизированного списка является то, что функция typeof возвра- щает текущий тип списка. В следующем примере мы проверяем что функция type возвра- щает 16, что соответствует списку. Но функция typeof возвращает строку «person».
- - > t y p e ( p )
ans
=
16.
- - > t y p e o f ( p )
ans
=
p e r s o n
Это позволяет динамически менять поведение функций для типизированных списков с ти- пом «person». Эта возможность связана с перегрузкой функций, темой, которая будет рас- смотрена в разделе
3.8
Теперь мы рассмотрим функции, которые позволяют динамически получать инфор- мацию о типовых списках. Для того, чтобы получить список полей «person», мы можем
35
использовать синтаксис p(1) и получить матрицу строк размером 1 × 4, как в следующем примере.
- - > p (1)
ans
=
! p e r s o n f i r s t n a m e n a m e b i r t h y e a r
!
Тот факт, что первый строковый элемент представляет тип, может быть полезен или мешать,
в зависимости от ситуации. Если мы только хотим получить поля типизированного списка
(и не распечатывать), то мы можем использовать функцию fieldnames.
- - > f i e l d n a m e s ( p )
ans
=
! f i r s t n a m e
!
!
!
! n a m e
!
!
!
! b i r t h y e a r
!
Когда мы создаём типизированный список, мы можем определить его поля без установ- ки его значений. Действующее значение поля может быть на самом деле позже установлено динамически в файле-сценарии. В этом случае было бы полезно знать, определено ли поле уже ли нет.
Следующий пример показывает как функция definedfields возвращает матрицу це- лых чисел с плавающей запятой, представляющую поля, которые уже определены. Мы на- чинаем с определения p человека без установки какого-либо значения любого из полей.
Вот почему единственное определённое поле равно числу 1. Затем мы устанавливаем поле
«firstname», которое соответствует индексу 2.
- - > p = t l i s t ([ " p e r s o n " , " f i r s t n a m e " , " n a m e " , " b i r t h y e a r " ])
p
=
p (1)
! p e r s o n f i r s t n a m e n a m e b i r t h y e a r
!
- - > d e f i n e d f i e l d s ( p )
ans
=
1.
- - > p . f i r s t n a m e = " P a u l " ;
- - > d e f i n e d f i e l d s ( p )
ans
=
1.
2.
Как мы можем видеть, индекс 2 был добавлен к матрице с помощью определённых полей,
возвращённых функцией definedfields.
Функции, которые мы рассмотрели, позволяют программировать типизированные спис- ки очень динамичным образом. Теперь мы увидим как использовать функции definedfields для динамического вычисления определено ли поле, идентифицированное по его строке,
или нет. Это позволит получить немного больше практики с типизированными списками.
Вспомним, что мы можем создать типизированный список без действительного определения значений его полей. Эти поля могут быть определены позже, так что в конкретное время мы не знаем все ли поля определены или нет. Следовательно, может потребоваться функция isfielddef, которая бы вела себя как в следующем примере.
- - > p = t l i s t ([ " p e r s o n " , " f i r s t n a m e " , " n a m e " , " b i r t h y e a r " ]);
- - > i s f i e l d d e f ( p , " n a m e " )
ans
=
F
36

- - > p . n a m e = " S m i t h " ;
- - > i s f i e l d d e f ( p , " n a m e " )
ans
=
T
Целью упражнения
3.2
является динамическое определение: существует ли в типизи- рованном списке поле, связанное с заданным полем, определённым по его строке.
3.7
Имитация объектно-ориентированного программирования с по- мощью типизированных списков
В этом разделе мы рассмотрим как типизированные списки могут использоваться для имитации объектно-ориентированного программирования (ООП). Это обсуждение частично было представлено в вики Scilab [
7
].
Мы представляем простой метод имитирования ООП с текущими функциями Scilab’а.
Предлагаемый метод является классическим, когда мы хотим имитировать ООП на не-ООП
языке, например, на Си или фортране. В первой части мы анализировали ограничения функ- ций, которые используют позиционные аргументы. Далее мы представляем метод имитации
ООП в Scilab с помощью типизированных списков.
В заметках, связанных с этим разделом, мы представляем похожие методы на дру- гих языках. Мы подчёркиваем тот факт, что объектно-ориентированное программирование использовалось и используется десятками не-ООП языков, таких как Си или фортран, на- пример, методами, которые очень похожи на тот, что мы собираемся представить.
3.7.1
Ограничения позиционных аргументов
Перед тем как перейти к деталям, мы сначала представим причины того, почему ими- тация ООП в Scilab’е удобна и, иногда, необходима. В самом деле, метод, который мы защи- щаем, может позволить упростить многие функции, которые основаны на необязательных,
позиционных аргументах. Факт, что выполнение, основанное на позиции входных и выход- ных аргументов функции, является жёстким ограничением в некоторых случаях, как мы вскоре увидим.
Например, примитив optim является встроенной функцией, которая выполняет неогра- ниченную и частично ограниченную числовую оптимизацию. Эта функция имеет 20 аргу- ментов, некоторые из которых необязательные. Далее приводим заголовок функции, где квадратные скобки [...] обозначают необязательные параметры.
[ f [ , x o p t [ , g r a d o p t [ , w o r k ] ] ] ] = ..
o p t i m ( c o s t f [ , < contr >] , x0 [ , a l g o ] [ , df0 [ , mem ]] [ , w o r k ] ..
[ , < stop >] [ , < params >] [ , imp = i f l a g ])
Эта усложнённая последовательность вызова делает практическое использование optim сложным (но работоспособным), особенно если мы хотим настроить его аргументы. Напри- мер, переменная является списком четырёх необязательных аргументов:
’ ti ’ , v a l t i , ’ td ’ , v a l t d
Многие параметры алгоритма могут быть сконфигурированы, но, довольно удивитель- но, что многие не могут быть сконфигурированы пользователем функции optim. Например,
в случае квази-Ньютоновского алгоритма без ограничений, процедуры фортрана позволяют сконфигурировать длину, представляющую оценку расстояния до оптимума. Этот параметр не может быть сконфигурирован на уровне Scilab и по умолчанию используется значение
37

0,1. Причина, которая кроется за этим выбором, очевидна: есть уже слишком много пара- метров для функции optim и добавление других необязательных параметров привело бы к невозможности пользования функцией.
Более того, пользователи и разработчики хотят добавлять новые возможности в при- митив optim, но это может привести к нескольким трудностям.
• Расширение текущего шлюза optim очень сложно из-за сложного управления
20-ю внутренними необязательными аргументами. Более того, поддержка и разработ- ка интерфейса в течение жизни проекта Scilab трудны, поскольку порядок аргумен- тов имеет значение. Например, мы можем осознавать, что один аргумент может быть ненужным (из-за того, например, что эта же самая информация может быть получена через другую переменную). В этом случае мы не можем удалить аргумент из после- довательности вызова, поскольку это сломает обратную совместимость всех файлов- сценариев, которые используют эту функцию.
• Трудно расширить список выходных аргументов. Например, нас может заинтересо- вать целое число, представляющее статус оптимизации (например, достигнута ли схо- димость, достигнуто ли максимальное число итераций, максимальное число вызовов функций и т. д.). Мы могли бы быть также заинтересованы в числе итераций, чис- ле вызовов функций, конечном значении аппроксимации матрицы Гессе, и в другой информации. Ограниченное число выходных аргументов на самом деле ограничивает количество информации, которую пользователь может вытянуть из имитации. Более того, если мы захотим получить выходной аргумент №6, например, то мы должны вызвать функцию со всеми аргументами от №1 до №6. Это может породить много ненужных данных.
• Мы могли бы захотеть установить необязательный входной аргумент №7, а не необяза- тельный аргумент №6, который отсутствует в данном интерфейсе. Это из-за того, что система обработки аргументов основана на порядке аргументов.
В общем и целом, факт, что входные и выходные аргументы используются явно и на основе их последовательности, что очень неудобно когда число аргументов велико.
Если окружение объектно-ориентированного программирования было бы доступно для
Scilab’а, то управление аргументами было бы решено с меньшими трудностями.
3.7.2
Класс «person» в Scilab
В этом разделе мы даём конкретный пример метода, основанного на разработке класса
«person».
Метод, который мы представляем в этом документе, для имитации объектно-ориенти- рованного программирования является классическим для других языков. На самом деле о общий для расширения необъектных языков в ООП-системе. Возможный метод:
• мы создаём тип абстрактных данных (ТАД) со структурами основных данных языка,
• мы имитируем методы с помощью функций, у которых первый элемент, по имени this,
представляет текущий объект.
Мы будем использовать этот метод и имитировать в качестве примера отдельный класс
«person».
Класс «person» сделан из следующих функций.
38

• Функция person_new, «конструктор», создаёт новый объект «person».
• Функция person_free, «деструктор», разрушает существующий объект «person».
• Функция person_configure и функция person_cget, позволяют сконфигурировать и опросить поля существующего объекта «person».
• Функция person_display, «метод», который отображает текущий объект в консоли.
В этих функциях текущий объект будет храниться в переменной this. Чтобы реализовать наш класс, мы используем типизированный список.
Следующая функция person_new возвращает this нового объекта «person». Этот но- вый «person» определён по своим фамилии, имени, номеру телефона и адресу электронной почты. Мы выбираем для использования пустые значения строк для всех полей.
f u n c t i o n t h i s = p e r s o n _ n e w ()
t h i s = t l i s t ([ " T P E R S O N " , " n a m e " , " f i r s t n a m e " , " p h o n e " , " e m a i l " ])
t h i s . n a m e = " "
t h i s . f i r s t n a m e = " "
t h i s . p h o n e = " "
t h i s . e m a i l = " "
e n d f u n c t i o n
Следующая функция person_free разрушает существующий объект «person».
f u n c t i o n t h i s = p e r s o n _ f r e e ( t h i s )
// Нет никаких действий.
e n d f u n c t i o n
Поскольку сейчас нет никаких действий, то тело функции person_free пустое. По-прежнему,
в силу разумных причин и из-за того, что реальное тело функции может развиться позже во время разработки компонента, мы создаём эту функцию в любом случае.
Мы подчёркиваем, что переменная this является как входным, так и выходным аргу- ментом функции person_free. В самом деле, текущий объект «person», в принципе, является модифицируемым через работу функции person_free.
Функция person_configure позволяет сконфигурировать поле текущего объекта «person».
Каждое поле идентифицируется строкой, «ключом», который соответствует значению. Сле- довательно, это просто отображение «ключ-значение». Функция устанавливает значение value, соответствующее заданному ключу key, и возвращает обновлённый объект this. Для класса «person» ключами являются "-name", "-firstname", "-phone" и "-email".
f u n c t i o n t h i s = p e r s o n _ c o n f i g u r e ( this , key , v a l u e )
s e l e c t key c a s e " - na m e " t h e n t h i s . n a m e = v a l u e c a s e " - f i r s t n a m e " t h e n t h i s . f i r s t n a m e = v a l u e c a s e " - p h o n e " t h e n t h i s . p h o n e = v a l u e c a s e " - e m a i l " t h e n t h i s . e m a i l = v a l u e e l s e e r r m s g = s p r i n t f ( "Неизвестный ключ %s " , key )
e r r o r ( e r r m s g )
end e n d f u n c t i o n
39

Мы выбрали префиксацию каждого ключа знаком минус «-». В последовательностях вызова это позволит легко отличить ключ от значения.
Мы подчёркиваем, что переменная this является как входным, так и выходным ар- гументом функции person_configure. Она похожа на функцию person_free, которую мы создали прежде.
Аналогично, функция person_cget позволяет получить заданное поле текущего объ- екта «person». person_cget возвращает значение value, соответствующее заданному ключу key текущего объекта.
f u n c t i o n v a l u e = p e r s o n _ c g e t ( this , key )
s e l e c t key c a s e " - n am e " t h e n v a l u e = t h i s . n a m e c a s e " - f i r s t n a m e " t h e n v a l u e = t h i s . f i r s t n a m e c a s e " - p h o n e " t h e n v a l u e = t h i s . p h o n e c a s e " - e m a i l " t h e n v a l u e = t h i s . e m a i l e l s e e r r m s g = s p r i n t f ( "Неизвестный ключ %s " , key )
e r r o r ( e r r m s g )
end e n d f u n c t i o n
Точнее, функция person_cget позволяет получить значение конфигурируемого ключа. «c»
в «cget» относится к первой букве «configure» (настроить). Если, вместо этого, мы захо- тим создать функцию, которая возвращает значение ненастраиваемого ключа, мы можем назвать её person_get.
Теперь, когда наш класс установлен, мы можем создать новый «метод». Следующая функция person_display распечатать текущий объект this в консоли.
f u n c t i o n p e r s o n _ d i s p l a y ( t h i s )
m p r i n t f ( " P e r s o n \ n " )
m p r i n t f ( " N a m e : %s \ n " , t h i s . n a m e )
m p r i n t f ( " F i r s t n a m e : %s \ n " , t hi s . f i r s t n a m e )
m p r i n t f ( " P h o n e : %s \ n " , t h i s . p h o n e )
m p r i n t f ( " E - m a i l : %s \ n " , t h i s . e m a i l )
e n d f u n c t i o n
Теперь мы представим простое использование класса «person», который мы только что разработали. В следующем примере мы создаём новый объект «person» с помощью вызова функции person_new . Затем мы вызовем функцию person_configure несколько раз для того, чтобы сконфигурировать различные поля объекта «person».
p1 = p e r s o n _ n e w ();
p1 = p e r s o n _ c o n f i g u r e ( p1 , " - n am e " , " B a c k u s " );
p1 = p e r s o n _ c o n f i g u r e ( p1 , " - f i r s t n a m e " , " J o h n " );
p1 = p e r s o n _ c o n f i g u r e ( p1 , " - p h o n e " , " 0 1 . 2 3 . 4 5 . 6 7 . 8 9 " );
p1 = p e r s o n _ c o n f i g u r e ( p1 , " - e m a i l " , " j o h n . b a c k u s @ c o m p a n y . com " );
В следующем примере мы вызовем функцию person_display и распечатаем текущий «person».
- - > p e r s o n _ d i s p l a y ( p1 )
P e r s o n
N a m e : B a c k u s
40

F i r s t n a m e : J o h n
P h o n e : 0 1 . 2 3 . 4 5 . 6 7 . 8 9
E - m a i l : j o h n . b a c k u s @ c o m p a n y . com
Мы можем также запросить имя текущего «person» вызовом функции person_get.
- - > n a m e = p e r s o n _ c g e t ( p1 , " - n a me " )
n a m e
=
B a c k u s
Наконец, мы уничтожим текущий «person».
p1 = p e r s o n _ f r e e ( p1 );
3.7.3
Расширение класса
В этом разделе мы обсудим способы расширения класса, основанные на имитации ме- тода, который мы представили. Наша цель — возможность управлять более сложными ком- понентами с б´
ольшим количеством полей, б´
ольшим количеством методов или б´
ольшим ко- личеством классов.
Сперва мы подчеркнём, что управление опциями класса безопасно. На самом деле система, которую мы только что разработали, просто сопоставляет значение ключу. Следо- вательно, список ключей, определяется один раз для всех, пользователь не может конфи- гурировать или получать значение ключа, который не существует. Если мы попытаемся, то функции person_configure или person_cget сформируют ошибку.
Новый ключ в класс добавляется напрямую. Сначала мы должны обновить функцию person_new, добавив новое поле к типизированному списку. Мы можем решить, какое зна- чение по умолчанию использовать для нового поля. Заметим, что существующие файлы- сценарии, использующие класс «person», будут работать. Если требуется, то файл-сценарий может быть обновлён для того, чтобы конфигурировать новый ключ значением не по умол- чанию.
Мы можем решить различать публичные поля, которые могут быть сконфигурированы пользователем или классом, и частные поля, которые не могут. Для того, чтобы добавить новое частное поле в класс «person», скажем «bankaccount» (банковский счёт), мы изменя- ем функцию person_new и добавляем соответствующую строку в типизированный список.
Следовательно, нам не нужно делать его доступным ни в функции person_configure, ни в функции person_cget, пользователь класса не сможет получить доступ к нему.
Мы можем быть немного гибче, позволяя пользователю класса получать значения по- лей без возможности изменения значения. В этом случае нам следует создать отдельную функцию person_get (заметим отсутствие буквы «c»). Это позволяет отделить конфигури- руемые опции от неконфигурируемых опций.
Мы можем создать даже более сложные структуры данных с помощью вложения клас- сов. Это легко, поскольку типизированные списки могут содержать типизированный список,
который может содержать типизированный список, и т. д. до любого требуемого уровня вло- жения. Следовательно, мы можем имитировать ограниченное наследование, идея, которая является одним из главных вопросов в ООП.
К примеру, допустим, что мы хотим создать класс «company». Эта «company» опре- деляется по её имени, адресу, назначению и списком всех людей («person»), работающих в ней. Следующая функция company_new предлагает возможную реализацию.
1   2   3   4   5   6   7   8   9   ...   13


написать администратору сайта