Элементы управления и динамика
Второй аргумент в Dynamic[]. Когда Dynamic[] используется вместе с ползунком в виде Slider[Dynamic[expr]] , при движении ползунка все время выполняется присвоение expr = new, где new - значение, вычисленное по положению ползунка. Если присвоение успешно, ползунок смещается, иначе - остается на месте.
В приведенном ниже примере можно двигать лишь левый ползунок. Попытка движения правого приводит к ошибке, так как невозможно присвоить новое значение выражению 1-x.
In[43] : = DynamicModule [{х = 0} , {Slider[Dynamic[x]] , Slider[Dynamic[1 - х] ]} ]
А именно, если попробовать пошевелить второй слайдер, то возникнет сообщение об ошибке, типа
During evaluation of In [133]:= Set::write : Tag Plus in -0.202 +1 is Protected. >>
Однако если использовать второй аргумент команды Dynamic, то с помощью него можно задать динамику первого аргумента:
In[44]:= DynamicModule[{х = 0}, {Slider [Dynamic [x] ] , Slider [Dynamic [1 - х, (х = 1 -#) &] ] } ]
Этим способом можно реализовать обратную функцию:
In[45]:= DynamicModule\{х = 0}, {Slider[Dynamic[x]], Slider [Dynamic [1 - 2 х, (х = (1 -#) / 2) &] , {-1, 1}] }]
Таким образом, формат команды следующий: Dynamic[expr, f] , где f[expr, val] вычисляется при каждом изменении значения val, которое, в свою очередь, задается положением ползунка.
В приводимом примере используем тот факт, что DateList[], выдающая временные данные в формате год, месяц, день, час, минута, секунда, так же как и функция RandomReal[], самостоятельно не обновляется. Однако при обновлении второго аргумента динамики первый также пересчитывается:
In[46] := Slider [Dynamic [x] ] Dynamic[DateList[], Dynamic[x]]
Out[47]= {2010, 2, 3, 18, 53, 43.5625000}
Можно заставить самостоятельно обновляться Dynamic[], задав опцию UpdateInterval:
- (обновляться не реже чем каждые секунд),
- (обновляться как можно чаще),
- (не обновляться).
In[48]: = Dynamic[DateList [] , UpdateInterval -> 1] Out[48]= {2010, 2, 3, 18, 53, 43.6093750}
Где нужно располагать Dynamic[]? Оператор Dynamic[] ведет себя достаточно хитро, и далеко не каждое его расположение приводит к ожидаемому результату. Опишем несколько установленных экспериментально важных принципов, которые позволяют избежать многочисленных ошибок.
- Mathematica состоит из оболочки ( Front End ), в которой мы сейчас работаем, и ядра ( Kernel ), выполняющего вычисления выражений (перед тем как выполнится первая команда, скажем, 2+2, набранная в оболочке и активированная командой оболочки Shift+Enter, Mathematica загружает в память свое ядро и команда обрабатывается именно ядром; в дальнейшем ядро остается в памяти и производит вычисление выражений, активизированных той же командой Shift+Enter ). Используя команду меню Evaluation/Quit Kernel/Local, можно выгрузить ядро, завершив тем самым сеанс. Если затем выполнить команду Shift+Enter, позиционировав предварительно курсор в какой-нибудь клетке, ядро вновь загрузится и команды из клетки будут обработаны ядром.
-
Главная особенность оператора Dynamic[] состоит в том, что он выполняется оболочкой, а не ядром. В частности, невозможно вычислить функцию от переменной, обрамленной командой Dynamic[].
In[49]:=х = 10; N[Sin[Dynamic[x]]] Out[50] = Sin [0.5]
(хотя значение x подставляется в Sin[], тем не менее команда N[] не может вычислить значение Sin[10] ).
Функция Plot замечает ошибку сама:
In[51]:=х = . Plot[Dynamic[x2] , {х, -1, 1}]
-
Функция Dynamic[expr] имеет атрибут HoldFirst (см. подробности в лекции 4), поэтому аргумент expr не меняется при вычислении:
In[53] : = Attributes [Dynamic] Dynamic[1 + 1] // InputForm Out[53]= {HoldFirst, Protected, ReadProtected} Out[54] = Dynamic [1 + 1]
Это можно преодолеть с помощью команды Evaluate:.
In[55] : = Dynamic [Evaluate [1 + 1] ] // InputForrn Out[55] = Dynamic [2]
Важное следствие: ошибки при создании списка динамических данных (в примере временная переменная i внутри data[[i]] не меняет своего значения, потому что Dynamic имеет атрибут HoldFirst ).
In[56]:=data = {.1, .5, .3, .9, .2}; Dynamic[data] \\ Table[Slider[Dynamic [datap]] ]], {i, 5}] Out[57] = { 0 . 1, 0.5, 0.3, 0.9, 0.2}
Чтобы конструкция заработала, можно использовать правило замены так, чтобы итератор оказался снаружи Dynamic:
In[59]:=data ={.1, .5, .3, .9, .2}; Dynamic[data] Table [Slider [Dynamic [data [[i]]] ] /. i -> ii, {ii, 5}] Out[60] ={ 0 . 1, 0.5, 0.3, 0.9, 0.2}
Еще один вариант - использовать команду With[{x=x0,y=y0,...},expr] , состоящую в том, что при каждом появлении символов x, y, ... они заменяются на x0, y0, ...:
In[62]: = With[{y = р - 1}, 1+у-у^2] Out[62] = -(-l+p)2+p In[63]: = data = { .1, .5, .3, .9, .2}; Dynamic[data] Table [With [{i = i}, Slider [Dynamic [data [[i]] ] ] ] , {i, 5}] Out[64] = {0 .1, 0.5, 0.3, 0.9, 0.2}
-
Dynamic не выводится при стандартном выводе, хотя на самом деле присутствует, что можно увидеть, заменив формат вывода на InputForm. Кроме того, при выводе в стандартном режиме аргумент expr выражения Dynamic[expr] визуализируется в вычисленном виде, несмотря на то, что реально Dynamic[expr] представлено невычисленным выражением expr:
In[66]:=Dynamic [1+1] Dynamic[1+1]//InputForm Out[66]=2 Out[67]=Dynamic [1+1] In[68]:=x =. In[69]:=2+Slider[x] Out[69]= 2 + Slider[x] In[70]:=Slider[x] Out[70]=Slider[x]
-
При использовании Dynamic[] с элементами управления нужно придерживаться следующего правила: первый аргумент элемента управления почти всегда обрамлен командой Dynamic, например, Slider[Dynamic[x]] , Checkbox[Dynamic[x]] , RadioButton[Dynamic[x]] . ..:
In[71]:={Dynamic [Slider [x] ],Dynamic[x]}
-
Расположение Dynamic в правильном месте - высокое искусство. Часто это - самый внешний оператор, хотя, как мы уже видели на примере элементов управления, это не всегда так. Кроме того, иногда имеется свобода в расположении Dynamic, а также операторы Dynamic могут быть вложенными в друг друга:
In[72]: = Dynamic [Table [x, {i, 10}]] Table[Dynamic[x], {i, 10}] Out[72]={0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5} Out[73] = {0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5}
В первом случае Dynamic применяется ко всей таблице, и обновление, происходящее при каждом изменении x, сопровождается пересчетом всей таблицы Table. Во втором случае Table работает один раз, создавая 10 динамических переменных Dynamic[x] , каждая из которых обновляется независимо при изменении x. В первом случае при изменении x посылается только один сигнал ядру, требующий пересчета всей таблицы. Во втором случае посылается много сигналов, требующих пересчета каждой динамической переменной. Который из способов более эффективен, зависит от конкретного случая.
-
Dynamic может быть расположен в правой части опций, однако далеко не во всех случаях. Например, Dynamic не может регулировать количество PlotPoints, потому что функция Plot должна знать значение PlotPoints до того, как будет порождено то, что потом изобразится на экране (напомним, что Dynamic выполняется оболочкой, а не ядром). Приведем примеры возможного использования Dynamic в опциях:
In[74]:= {Slider [Dynamic [h] , {6, 100}], Dynamic[Style["Это - текст", FontSize -> h]]} {Slider[Dynamic[h], {6, 100}], Style["Это - текст", FontSize -> Dynamic [h] ] }
(Команда Style работает с оболочкой, настраивая вид вывода, поэтому Style может работать с Dynamic.)
Dynamic может быть также использована с командой SetOptions:
In[76] := SetOptions [Graphics , Background -> Dynamic [Hue [x] ] ] {Slider[Dynamic[x]], Graphics[Circle[]]} Out[76] = {AlignmentPoint -> Center, AspectRatio -> Automatic, Axes -> Feilse, AxesLabel -> None, AxesOrigin -> Automatic, AxesStyle -> { } , Background -> Hue [0.5], BaselinePosition -> Automatic, BaseStyle -> {}, ColorOutput -> Automatic, ContentSelectable -> Automatic, CoordinatesToolOptions -> Automatic, DisplayFunction : -> $DisplayFunction, Epilog -> {}, FormatType : -> TraditionalForm, Frame -> False, FrameLabel -> None, FrameStyle -> {}, FrameTicks -" Automatic, FrameTicksStyle -> {}, GridLines -> None, GridLinesStyle -> {}, ImageMargins -> 0 ., ImagePadding -> All, ImageSize -> Automatic, ImageSizeRaw -> Automatic, LabelStyle -> {}, Method -> Automatic, PlotLabel -> None, PlotRange -> All, PlotRangeClipping -> False, PlotRangePadding -> Automatic, PlotRegion -> Automatic, PreservelmageOptions -> Automatic, Prologs -> {}, RotateLabel -> True, Ticks -> Automatic, TicksStyle -> {}}
In[78] : = Clear [h, x]