OCAML: разница между пусть func a x = (a x);; и пусть func a x = ((fun a -> a) x);;?

#function #parameters #ocaml

#функция #параметры #ocaml

Вопрос:

Я немного застрял на этом задании для OCAML. Я пытаюсь передать функцию и значение в качестве параметра в другую функцию. Например, у меня есть функция с именем test, которая принимает (fun x -> x x) и 3 в качестве параметров. Функциональный тест должен вывести 6, поскольку 3 3 = 6. Я знаю, что могу добиться чего-то подобного, выполнив:

 let func x = x   x;;

let a = func;;

let test a x = (a x);;
  

Таким образом, когда я ввожу, проверяю 5, я получу 10.

Но когда я меняю оператор на this, я получаю только значение, которое я ввел для x. Как мне заставить (fun a -> a) принимать значение x?

let test a x = ((fun a -> a) x);;

Ответ №1:

fun a -> a является анонимной функцией идентификации, она всегда будет возвращать свой параметр. Вы могли бы сказать:

 let id = fun a -> a;;
id 3;;
  => 3
id (fun x -> x   x);;
  => (fun x -> x   x)
  

Обратите внимание, что в вашем

 let test a x = ((fun a -> a) x);;
  

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

 let test a x = ((fun b -> b) x);;
  

Как мне заставить (fun a -> a) принимать значение x?

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

 let func x = x   x;;
let test a x = a x;;
test func 3;;
  => 6
  

Ответ №2:

С функциональными языками в простых случаях часто бывает полезно записать выражение в форме, подобной лямбда-исчислению, и выполнить сокращения вручную (отметив, какие сокращения вы используете). Вы все равно можете использовать синтаксис OCaml как упрощенную версию лямбда-исчисления

Итак, в случае вашего примера это станет:

    let test a x = ((fun a -> a ) x);;
=> let test a x = ((fun b -> b ) x);; (* Variable renamed (alpha equivalence) *)
=> let test a y = ((fun b -> b ) y);; (* Variable renamed (alpha equivalence) *)

   let func x = x   x;;
  

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

    test func 5
=> test (fun x -> x   x) 5 (* Variable func inserted (beta reduction) *)
=> (fun a y -> ((fun b -> b) y) (fun x -> x   x) 5 (* Variable test inserted *)
=> (fun y -> (fun b -> b) y) 5 (* Variable a inserted *)
=> ((fun b -> b) 5 (* Variable y inserted *)
=> 5 (* Variable b inserted *)
  

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

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

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