Здравствуйте, VladD2, Вы писали:
VD>Это новости уже лет пять.
Эта как раз-таки свежая. Раньше развитием языка занимался фактически один человек, теперь MS решила повернуться к нему если не лицом, то уж хотя бы не задом, и предоставить больше ресурсов для развития (хотя, конечно, в мэйнстрим ему не попасть).
Здравствуйте, Кодёнок, Вы писали:
Кё>Здравствуйте, palm mute, Вы писали:
Кё>>>Кто-нить объяснит, зачем ml требует писать «let rec». Неужели и так не ясно, rec или не rec.
Кё>я про рекурсивные функции
А что меняется? Функции — не функции — это неважно, все дело в видимости имен. Хорошо, изменим пример.
Теперь у нас будут 3 разных функции foo:
let foo () = 1
let _ = print_int (foo ())
let foo () = foo () + 1
let _ = print_int (foo ())
let foo () = foo () + 2
let _ = print_int (foo ())
Вывод этого кода такой же, как и в предыдущем случае.
Функция let foo () = foo () + 1 вызывает одноименную функцию foo, определенную выше. Функция let rec foo () = foo () + 1 вызывает сама себя.
Здравствуйте, palm mute, Вы писали:
Кё>>я про рекурсивные функции PM>А что меняется? Функции — не функции — это неважно, все дело в видимости имен. Хорошо, изменим пример.
А в форте для этого было спецслово RECURSE — оно не декларировалось рекурсивным, а просто для того, чтобы позвать себя надо было писать RECURSE. Кстати, интересно, что в ML надо сделать, чтоб звать как себя так и предыдущий вариант одноименной функции?
Здравствуйте, no4, Вы писали:
no4>Кстати, интересно, что в ML надо сделать, чтоб звать как себя так и предыдущий вариант одноименной функции?
перед определением рекурсивной функции старую переименовать:
let foo _ = ...
let foo_old = foo
let rec foo = ... (foo_old x) ... (foo y)
Хотя обычно такой проблемы не возникает, обычно все функции имеют различные имена. Потому Хаскелевский подход — все определения верхнего уровня обернуть в неявный letrec — вызывает меньше вопросов.
Здравствуйте, Кодёнок, Вы писали:
Кё>Кто-нить объяснит, зачем ml требует писать «let rec». Неужели и так не ясно, rec или не rec.
Вот игрушечный интерпретатор ML-подобного языка. Обратите внимание, насколько по-разному обрабатываются конструкции let и letrec в энергичном языке.
В ленивом языке гораздо проще выбросить let и оставить только letrec, т.к. бОльшие классы рекурсивных значений имеют смысл. Если же компилятор будет автоматически выбирать между let и letrec, это усложнит и сделает непоследовательными правила поиска имен для обычных и функциональных значений.
type varname = string
type expr =
Int of int |
Bool of bool |
Var of varname |
Plus of expr * expr |
Mult of expr * expr |
Eq of expr * expr |
If of expr * expr * expr |
Lambda of varname * expr |
Application of expr * expr |
Let of varname *expr * expr |
LetRec of varname * expr * expr
type value = VInt of int | VBool of bool | VClosure of (value -> value)
type binding = varname * value
type environment = binding list
exception EvalError of string
let error msg = raise (EvalError msg)
let internal_error () = failwith "The world is upside down"let lookup_var (var : varname) (env : environment) : value =
try
List.assoc var env
with Not_found -> error ("Unbound variable " ^ var)
let rec eval (e : expr) (env : environment) : value = match e with
| Int x -> VInt x
| Bool x -> VBool x
| Var name -> lookup_var name env
| Plus (e1, e2) ->
(match (eval e1 env, eval e2 env) with
| (VInt x, VInt y) -> VInt (x+y)
| _ -> error "Non-int in addition")
| Mult (e1, e2) ->
(match (eval e1 env, eval e2 env) with
| (VInt x, VInt y) -> VInt (x*y)
| _ -> error "Non-int in multiplication")
| Eq (e1, e2) ->
(match (eval e1 env, eval e2 env) with
| (VInt x, VInt y) -> VBool (x==y)
| _ -> error "Non-int in comparision")
| If (cond, thenExpr, elseExpr) ->
(match eval cond env with
| VBool x -> if x then eval thenExpr env
else eval elseExpr env
| _ -> error "Non-boolean in conditional")
| Lambda (var, body) ->
VClosure (fun x -> eval body ((var, x)::env))
| Application (func, arg) ->
(match eval func env with
| VClosure f -> f (eval arg env)
| _ -> error "Non-function in application")
| Let (var, value, body) ->
eval body ((var, eval value env)::env)
| LetRec (var, value, body) ->
(match value with
| Lambda _ ->
let rec new_env =
let rec func x =
match eval value new_env with
| VClosure f -> f x
| _ -> internal_error ()
in ((var, VClosure func)::env)
in eval body new_env
| _ -> error "Only functions are allowed on the right-hand side of letrec")
(*** tests *********************************)
(* fun n -> if n==0 then 1 else n * fact (n + (-1)) *)let (factorial_body : expr) =
(Lambda ("n",
If (Eq (Var "n", Int 0),
Int 1,
(Mult (Var "n",
Application (Var "fact",
Plus (Var "n", Int (-1))))))))
let tests =
[
(* let x = 5 in x + 4 *)
Let ("x", Int 5, Plus (Var "x", Int 4));
(* non-recursive function: let f = fun x -> x*2 in f 4 *)
Let ("f", Lambda ("x", Mult (Var "x", Int 2)), Application (Var "f", Int 4));
(* incorrect factorial *)
Let ("fact", factorial_body, (Application (Var "fact", Int 5)));
(* correct factorial *)
LetRec ("fact", factorial_body, (Application (Var "fact", Int 5)));
]
let print_val = function
| VInt n -> print_int n
| VBool b -> print_string (if b then"true"else"false")
| VClosure _ -> print_string "<function>"let run_eval expr =
try
(print_val (eval expr []);
print_endline "")
with EvalError msg -> print_endline msg
let main () = List.iter run_eval tests
let _ = main ()
Здравствуйте, palm mute, Вы при обсуждении вопроса "зачем нужен rec" вставили в код лишний rec — иронично, не правда ли?:
PM> | LetRec (var, value, body) ->
PM> (match value with
PM> | Lambda _ ->
PM> let rec new_env =
PM> let(* rec *) func x = (* здесь rec ни к чему *)
PM> match eval value new_env with
PM> | VClosure f -> f x
PM> | _ -> internal_error ()
PM> in ((var, VClosure func)::env)
PM> in eval body new_env
PM> | _ -> error "Only functions are allowed on the right-hand side of letrec")
Здравствуйте, palm mute, Вы писали:
PM>Вот игрушечный интерпретатор ML-подобного языка. Обратите внимание, насколько по-разному обрабатываются конструкции let и letrec в энергичном языке. PM>В ленивом языке гораздо проще выбросить let и оставить только letrec, т.к. бОльшие классы рекурсивных значений имеют смысл. Если же компилятор будет автоматически выбирать между let и letrec, это усложнит и сделает непоследовательными правила поиска имен для обычных и функциональных значений.
Вот неужели трудно было сделать примитивнейшую оптимизацию, посчитав любой let за let rec, а потом автоматически выбросив rec у нерекурсивных (или у не взаимно рекурсивных) функций?
Здравствуйте, konsoletyper, Вы писали:
K>Вот неужели трудно было сделать примитивнейшую оптимизацию, посчитав любой let за let rec, а потом автоматически выбросив rec у нерекурсивных (или у не взаимно рекурсивных) функций?
let rec не имеет смысла для значений других типов (ОКамл позволяет создавать циклические структуры данных, но с жесткими ограничениями, справа от знака "=" не может быть вызовов функций, только конструкторы и значения, в SML, насколько я знаю, рекурсивными могут быть только функции).
Поэтому, если делать все функции автоматически рекурсивными, мы получаем разные правила поиска имен для функций и для "простых" значений:
let x = 1
let x = x + 5 (* здесь x справа от '=' ссылается на переменную x
в области видимости, результат = 6 *)let foo x = x
let foo x = if x > 0 then x * foo (x-1) (* а здесь, по-твоему, должен быть рекурсивный вызов -
с какой стати? функции - это те же переменные,
поиск имен должен работать однообразно независимо от типа *)
Здравствуйте, palm mute, Вы писали:
K>>Вот неужели трудно было сделать примитивнейшую оптимизацию, посчитав любой let за let rec, а потом автоматически выбросив rec у нерекурсивных (или у не взаимно рекурсивных) функций?
PM>let rec не имеет смысла для значений других типов (ОКамл позволяет создавать циклические структуры данных, но с жесткими ограничениями, справа от знака "=" не может быть вызовов функций, только конструкторы и значения, в SML, насколько я знаю, рекурсивными могут быть только функции).
Кроме того, не все функции являются синтаксическими функциями.
(* понять, что sum - это функция, можно только после вывода типов,
который, естественно, делается уже при построенном AST.
различие между let и letrec - синтаксическое.
*)let sum = List.fold_left (+) 0
(* а теперь представим себе следующую ситуацию *)let foo f y z = ...
let bar = ...
let bar = foo bar (* какой bar здесь имеется в виду? bar - функция, но на основании
одного лишь синтаксиса это определить невозможно *)
Таких проблем нет в C#, Java (или даже Nemerle), но там и конструкции let .. in нет.
В Хаскеле let .. in есть (а также есть where), но Haskell — ленивый.
Здравствуйте, EvilChild, Вы писали:
EC>Ты как обычно по ссылкам не ходишь, милок? Вся соль в этом:
Может быть. Просто интеграция у него была очень давно. Фиговая, првда.
EC>
EC>Somasegar, the head of the Developer Division at Microsoft, has announced the productization of F#.
Значение слоава "productization" от меня ускальзает. Это значит, что интеграция войдет в поставку студии? Или речь идет о том, что им банально дадут больше бабок?
В общем, в чем новость я так и не могу понять.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Schade, Вы писали:
S>Эта как раз-таки свежая. Раньше развитием языка занимался фактически один человек, теперь MS решила повернуться к нему если не лицом, то уж хотя бы не задом, и предоставить больше ресурсов для развития (хотя, конечно, в мэйнстрим ему не попасть).
Им и раньше не один человек занимался. К тому же эти люди были очень близки с реальными разработчиками из МС, напиример, с разработчиками из команды Шарпа. Вот только из тройки языков: Scala, Nemerle и F# последний является самым не проходным.
S>Кстати, F# здорово изменился за последнее время.
Что конкретно изменилось? И хорошо ли это?
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.