let rec a = integF (lazy (- ka*a)) 100.0
and b = integF (lazy (ka*a - kb*b)) 0.0
and c = integF (lazy (kb*b)) 0.0
and ka = 1.0
and kb = 1.0
Это – работающий пример, где функция
integF
возвращает интеграл (как некое вычисление в монаде моделирования) по заданной производной и начальному значению. Здесь мы видим, что (1) переменные можно задавать произвольно без указания зависимости, (2) обратные связи задаются через явную ленивость. Фактически есть еще третий очень важный пункт: (3) компилятор сам следит за разрешимостью системы (в отличие от того же Haskell). Мне особенно нравится этот пункт, поскольку он делает подобный метод пригодным для широкого практического применения. Например, у нас имеется eDSL, а неискушенные пользователи задают свои системы. Компилятор сам все проверит и укажет на ошибку в случае необходимости. Неоценимое свойство.Правда, в случае одной более сложной системы компилятор F# (v1.9.9.9) почему-то неправильно вывел переменные. Соответствующий bug report был отослан. Я думаю, что это – временное явление.
Рекуррентные отношения можно задавать и более хитрым способом, используя генераторы массивов:
let smoothNI (x: Lazy<Dynamics<float>>) (t: Lazy<Dynamics<float>>) n (i: Dynamics<float>) =
let rec s = [|
for k = 0 to n-1 do
if k = 0 then
yield integ (lazy ((x.Value - s.[k]) / (t.Value / (float n)))) i
else
yield integ (lazy ((s.[k-1] - s.[k]) / (t.Value / (float n)))) i |]
in s.[n-1]
Здесь возвращается функция (значение в монаде моделирования), которая называется экспоненциальной порядка n сглаживающей x по времени t с начальным значением i.
Мне нравится.
> компилятор сам следит за разрешимостью системы (в отличие от того же Haskell).
ОтветитьУдалитьЭто какое-то ОЧЕНЬ сомнительное утверждение. Нельзя ли развернуть?
Я имею ввиду, что компилятор F# проверяет (как оказалось, не всегда) на отсутствие циклических связей при определении переменных. Если есть цикл, то будет ошибка времени компиляции. Насколько знаю, в Haskell такого нет. Другая сторона медали - обратные связи в F# нужно явно указывать через ключевое слово lazy.
ОтветитьУдалитьНапример, такое не пройдет в F#:
let rec a = b + 1
and b = a
Хотя более упрощенная версия все же откомпилируется, хотя и не должна (!!):
let rec a = b
and b = a
Видимо, еще один баг. Сырая реализация. Но во всяком случае, все мои другие попытки задать циклы компилятор F# четко пресекал.
Кстати, похоже, что в ОCaml такой фичи с рекурсивным определением переменных вовсе нет.
На этой неделе сразу два письма отослал им. Получил ответ от Дона Сайма. Как я понял, последний случай из предыдущего комментария является полиморфным. Он обрабатывается иначе. Но если добавить аннотацию типа, то вылезет желаемая ошибка компиляции. Проверка на разрешимость будет работать. Более того, думаю, что в реальных условиях моей задачи она будет работать практически всегда, потому что в таких уравнениях не должны возникать полиморфные типы.
ОтветитьУдалить// OK: it won't be compiled!
let rec a: int = b
and b: int = a
Конечно, зависимости через лямбды не проверяются, но иначе и нельзя было бы добавить обратную связь для интегралов!
Надо сказать, что я не знаю других языков программирования общего назначения, где была бы такая крутая штука с проверкой разрешимости задаваемых рекурсивно систем переменных! В специализированных такое, конечно, есть. Например, в моем MapSim.