我对文档感到有些困惑fix
(虽然我认为我理解它现在应该做什么),所以我查看了源代码.这让我更加困惑:
fix :: (a -> a) -> a
fix f = let x = f x in x
Run Code Online (Sandbox Code Playgroud)
这究竟是如何返回固定点的?
我决定在命令行试一试:
Prelude Data.Function> fix id
...
Run Code Online (Sandbox Code Playgroud)
它挂在那里.现在公平地说,这是在我的旧Macbook上,这有点慢.但是,此功能也不能太过,因为任何东西传递给ID给出了同样的事情,回(更不用提,它的吃了没有CPU时间)计算昂贵.我究竟做错了什么?
在阅读"The Seasoned Schemer"时,我开始学习letrec
.我理解它的作用(可以用Y-Combinator复制),但本书正在使用它来代替在已define
保持静态参数的已经d函数上重复出现.
使用define
d函数重复出现的旧函数示例(没什么特别的):
(define (substitute new old l)
(cond
((null? l) '())
((eq? (car l) old)
(cons new (substitute new old (cdr l))))
(else
(cons (car l) (substitute new old (cdr l))))))
Run Code Online (Sandbox Code Playgroud)
现在有一个相同功能的例子,但使用letrec
:
(define (substitute new old l)
(letrec
((replace
(lambda (l)
(cond
((null? l) '())
((eq? (car l) old)
(cons new (replace (cdr l))))
(else
(cons (car l) (replace (cdr l))))))))
(replace lat)))
Run Code Online (Sandbox Code Playgroud)
除了稍微长一点,更难以阅读之外,我不知道他们为什么要在书中重写函数来使用letrec.通过这种方式在静态变量上重复出现时是否有速度增强,因为你没有继续传递它?
对于具有参数的函数,这种标准实践是保持静态还是减少了一个参数(例如重复列表的元素)?
来自更有经验的Schemers/LISPers的一些意见将有所帮助!
现在似乎有不少主流语言支持函数文字.它们也被称为匿名函数,但我不在乎它们是否有名称.重要的是函数文字是一个表达式,它产生一个尚未在别处定义的函数,因此例如在C中,&printf
不计算.
编辑添加:如果你有一个真正的函数文字表达式<exp>
,你应该能够将它传递给一个函数f(<exp>)
或立即将它应用于一个参数,即.<exp>(5)
.
我很好奇哪些语言可以让你编写递归的函数文字.维基百科的" 匿名递归 "文章没有给出任何编程示例.
我们以递归因子函数为例.
以下是我所知道的:
JavaScript/ECMAScript可以用callee
:
function(n){if (n<2) {return 1;} else {return n * arguments.callee(n-1);}}
Run Code Online (Sandbox Code Playgroud)在语言方面很容易letrec
,例如Haskell(称之为let
):
let fac x = if x<2 then 1 else fac (x-1) * x in fac
Lisp和Scheme中有等价物.请注意,绑定fac
是表达式的局部,因此整个表达式实际上是一个匿名函数.
还有其他人吗?
recursion language-features function-literal anonymous-function letrec
我有一个函数,它根据迭代计算一个固定点:
equivalenceClosure :: (Ord a) => Relation a -> Relation a
equivalenceClosure = fst . List.head -- "guaranteed" to exist
. List.dropWhile (uncurry (/=)) -- removes pairs that are not equal
. U.List.pairwise (,) -- applies (,) to adjacent list elements
. iterate ( reflexivity
. symmetry
. transitivity
)
Run Code Online (Sandbox Code Playgroud)
请注意,我们可以从这里抽象到:
findFixedPoint :: (a -> a) -> a -> a
findFixedPoint f = fst . List.head
. List.dropWhile (uncurry (/=)) -- dropWhile we have not reached the fixed point
. …
Run Code Online (Sandbox Code Playgroud) 假设我有一个像这样的愚蠢的小案例类:
case class Foo(name: String, other: Foo)
Run Code Online (Sandbox Code Playgroud)
我如何定义a
和b
不可改变这样a.other
的b
,而且b.other
是a
?scala是否提供了"打结"的方法?我想做这样的事情:
val (a, b): (Foo, Foo) = (Foo("a", b), Foo("b", a)) // Doesn't work.
Run Code Online (Sandbox Code Playgroud)
可能性
在Haskell中,我会这样做:
data Foo = Foo { name :: String, other :: Foo }
a = Foo "a" b
b = Foo "b" a
Run Code Online (Sandbox Code Playgroud)
绑定到a
和b
包含在同一let
表达式中或顶层的绑定.
或者,在不滥用Haskell的自动化letrec功能的情况下:
(a, b) = fix (\ ~(a', b') -> Foo "a" b', Foo "b" …
Run Code Online (Sandbox Code Playgroud) 运行此代码:
j = let x = 4
in let x = x * x
in x
Run Code Online (Sandbox Code Playgroud)
在翻译中:
ghci> j
... no response ...
Run Code Online (Sandbox Code Playgroud)
挂起CPU利用率很低.为什么是这样?我期待j = 16
.
这是Haskell中定点组合器的通常定义:
fix :: (a -> a) -> a
fix f = let x = f x in x
Run Code Online (Sandbox Code Playgroud)
在https://wiki.haskell.org/Prime_numbers上,他们定义了一个不同的定点组合子:
_Y :: (t -> t) -> t
_Y g = g (_Y g) -- multistage, non-sharing, g (g (g (g ...)))
-- g (let x = g x in x) -- two g stages, sharing
Run Code Online (Sandbox Code Playgroud)
_Y
是一个非共享的固定点组合器,在这里安排递归"伸缩"多级素数生产(生产者塔).
这到底是什么意思?在这种情况下,"共享"与"非共享"是什么?有_Y
什么不同fix
?
如何letrec
在不使用的情况下实施set!
?
在我看来,这set!
是一个命令式的编程结构,通过使用它,一个人失去了函数式编程的好处.
所以,据我了解,以下内容:let
,let*
,letrec
并letrec*
在方案/球拍使用人工合成的糖.
现在,如果我有一个简单的程序:
(let ((x 1)
(y 2))
(+ x y))
Run Code Online (Sandbox Code Playgroud)
它被翻译成:
((lambda (x y) (+ x y)) 1 2)
Run Code Online (Sandbox Code Playgroud)
如果我有:
(let* ((x 1)
(y 2))
(+ x y))
Run Code Online (Sandbox Code Playgroud)
它被翻译成:
((lambda (x) ((lambda (y) (+ x y))) 2) 1)
Run Code Online (Sandbox Code Playgroud)
现在,对于我的第一个问题,我理解一个letrec
表达式的含义,它允许一个人在let中使用递归,但我不明白它是如何完成的.什么是letrec
翻译成?
例如,会是什么
(letrec ((x 1)
(y 2))
(+ x y))
Run Code Online (Sandbox Code Playgroud)
被翻译成?
第二个问题是类似的letrec*
- 但letrec*
我不明白它究竟有何不同letrec
?而且,letrec*
表达式将被翻译成什么?
我不得不从提取用户名和电子邮件Either
的AuthResponse
。
我使用case of
它的构造:
let (uname, uemail) =
case getUserResponseJSON creds of
Right (GoogleUserResponse uname uemail) -> (uname, uemail)
_ -> ("", "")
Run Code Online (Sandbox Code Playgroud)
但我对uname
和都有此警告uemail
:
This binding for ‘uname’ shadows the existing binding
bound at src/Foundation.hs:240:12
|
242 | Right (GoogleUserResponse uname uemail) -> (uname, uemail)
|
Run Code Online (Sandbox Code Playgroud)
我希望这let (uname, uemail)
超出了范围case of
。
case
如果uname
和uemail
尚未定义,如何从块中获得此警告?