Рекурсивные анонимные функции в SML - PullRequest
9 голосов
/ 10 августа 2011

Можно ли писать рекурсивные анонимные функции в SML? Я знаю, что могу просто использовать синтаксис fun, но мне любопытно.

Я написал, как пример того, что я хочу:

val fact =
    fn n => case n of
                 0 => 1
               | x => x * fact (n - 1)

Ответы [ 4 ]

14 голосов
/ 11 августа 2011

Анонимная функция больше не является анонимной, когда вы связываете ее с переменная. А поскольку val rec - это просто производная форма fun без Разница, кроме внешнего вида, вы могли бы написать это с помощью синтаксис fun. Также вы можете сделать поиск по шаблону в fn выражениях как в case, так как случаи получены из fn.

Итак, во всей своей простоте вы могли бы написать свою функцию как

val rec fact = fn 0 => 1
                | x => x * fact (x - 1)

но это точно так же, как ниже, более читабельно (по моему мнению)

fun fact 0 = 1
  | fact x = x * fact (x - 1)

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

val rec fact : int -> int =
fn 0 => 1
 | x => x * fact (x - 1)

Как уже упоминалось в templatetypedef, это можно сделать с помощью фиксированной точки комбинатор. Такой комбинатор может выглядеть как

fun Y f =
    let
      exception BlackHole
      val r = ref (fn _ => raise BlackHole)
      fun a x = !r x
      fun ta f = (r := f ; f)
    in
      ta (f a)
    end

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

val res =
    Y (fn fact =>
       fn 0 => 1
        | n => n * fact (n - 1)
      )
      5                       

Код с фиксированной запятой и пример вычисления любезно предоставлены Мортеном Бронс-Педерсеном.


Обновленный ответ на ответ Джорджа Кангаса:

На известных мне языках рекурсивная функция всегда будет привязана к название. Удобный и обычный способ обеспечивается такими ключевыми словами, как "define", или "let", или "letrec", ...

Тривиально верно по определению. Если функция (рекурсивная или нет) не связана с именем, она будет анонимной.

Нетрадиционный, более анонимный способ - лямбда-связывание.

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

Ответ Джеспера Ринберга показывает лямбда-связывание; «анонимный» функция связывается с именами "f" и "fact" с помощью лямбды (называемой "fn" в SML).

Анонимная функция на самом деле является анонимной (не «анонимной» - без кавычек), и да, конечно, она будет связана в scope с любой функцией, на которую она передается в качестве аргумента. В любых других случаях язык был бы совершенно бесполезным. То же самое происходит при вызове map (fn x => x) [.....], в этом случае функция анонимной идентификации все еще фактически анонимна.

«Нормальное» определение анонимной функции (по крайней мере, согласно wikipedia ), говорящее, что она не должна быть привязана к идентификатору, немного слабое и должно включать неявное выражение «в текущая среда ".

Это на самом деле верно для моего примера, как видно из его запуска в mlton с аргументом -show-based для файла, содержащего только fun Y ... и val res ..

val Y: (('a -> 'b) -> 'a -> 'b) -> 'a -> 'b
val res: int32

Из этого видно, что ни одна из анонимных функций не связана в среде.

Более короткая альтернатива "lambdanonymous", которая требует запуска OCaml от "ocaml -rectypes":

(fun f n -> f f n) 
(fun f n -> if n = 0 then 1 else n * (f f (n - 1))
7;; Which produces 7! = 5040.

Похоже, вы совершенно не поняли идею оригинального вопроса:

Можно ли писать рекурсивные анонимные функции в SML?

И простой ответ - да. Сложный ответ (среди прочего?) Является примером того, как это делается с использованием комбинатора с фиксированной точкой, а не «лямбданонимным» (что бы это ни значило) примером, выполненным на другом языке, с использованием возможностей, даже удаленных в SML. *

8 голосов
/ 10 августа 2011

Все, что вам нужно сделать, это поставить rec после val, как в

val rec fact =
        fn n => case n of
                     0 => 1
                   | x => x * fact (n - 1)

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

2 голосов
/ 16 октября 2015
let fun fact 0 = 1
      | fact x = x * fact (x - 1)
in
  fact
end

Это рекурсивная анонимная функция.Имя 'fact' используется только для внутренних целей.

В некоторых языках (таких как Coq) в качестве примитива для рекурсивных функций используется 'fix', в то время как в некоторых языках (таких как SML) в качестве примитива используется recursive-let.Эти два примитива могут кодировать друг друга:

fix f => e   
  := let rec f = e in f end

let rec f = e ... in ... end 
  := let f = fix f => e ... in ... end 
0 голосов
/ 17 августа 2011

На языках, которые я знаю, рекурсивная функция всегда будет привязана к имени. Удобный и обычный способ обеспечивается ключевыми словами, такими как «define», или «let», или «letrec», ...

Нетрадиционный, более анонимный выглядящий способ - с помощью лямбда-связывания. Ответ Джеспера Ринберга показывает лямбда-связывание; «анонимная» функция связывается с именами «f» и «fact» с помощью лямбд (называемых «fn» в SML).

Более короткая альтернатива "lambdanonymous", для которой требуется OCaml, запущенный "ocaml -rectypes":

(fun f n -> f f n)
(fun f n -> if n = 0 then 1 else n * (f f (n - 1))
7;;

Который производит 7! = 5040.

...