我仍然是Haskell的新手,我正在努力解决问题。我尝试了一些东西,但我确信这是错误的。
问题:
foldr : f x NIL = x
foldr : f x (cons a list) = f a (foldr f x list)
让我们享受一些乐趣并结合多种不同语言的功能。
(1)借用课程笔记和Haskell,让我们定义一个函数foldl,
(a)foldl f x NIL = x
(b)foldl f x (cons a list) = foldl f (f x a) list
(2)借用Python,我们可以将字符串视为字符的序列(即列表),
“bar” ≡ (‘b’ ‘a’ ‘r’) ≡ [‘b’, ‘a’, ‘r’]
(3)借用Lisp,我们可以使用cons单元来表示或创建列表,
(‘b’ ‘a’ ‘r’) ≡ (cons ‘b’ (cons ‘a’ (cons ‘r’ nil)))
(4)借用Fortran,我们可以定义一个字符串连接函数//,
‘concate’//’nation’ ⇒‘concatenation’
(5)借用Scheme,定义一个切换函数,
f x y = f y x,
(define toggle (lambda (f x y) (f y x)))
也就是说,toggle函数在Lambda Calculus中切换它的参数,它是
|_f. |_x. |_y. f y x
因此,例如,
(f ‘back’ ‘drop’) ⇒(f ‘drop’ back’)
在这个问题中,请跟踪以下功能的执行/减少:
foldl (toggle //) nil (“foo” “bar” “baz”)
以下是我给出的提示:
foldl (toggle //) nil (“foo” “bar” “baz”)
步骤1:从(1b),左侧,foldl f x (cons a list)
with f = (toggle //), x = nil, and list = (“foo” “bar” “baz”)
步骤2:使用(3)递归扩展列表
步骤3:使用(1b),右侧,foldl f(f x a)列表
to recursively apply f, (toggle //), to the expanded list
步骤4:使用(5)在列表表达式中应用切换功能。
步骤5:使用(4),//来减少表达式
我得到了什么:
foldl (toggle //) nil (“foo” “bar” “baz”)
foldl f x (cons a list) – via (1b)
foldl f x(a list) – via (3)
foldl f (f x a) list – via (1b)
foldl (toggle //) (toggle // x a) list – via(5)
foldl x // // a list – via (4)
foldl nil a (“foo” “bar” “baz”)
我知道这不对,但我觉得我在附近。如果我能得到一些指导,我觉得我可能会失去理解并获得成功的理解。
答案 0 :(得分:2)
从
开始foldl (toggle //) nil (“foo” “bar” “baz”)
{ 1b }
= foldl (toggle //) ((toggle //) nil "foo") (“bar” “baz”)
...
("foo" "bar" "baz")
不是Haskell的列表! (有什么奇怪的"
?)(toggle //)
也不是运算符的Haskell语法(它是(toggle (//))
)(toggle //) nil "foo")
或foldl
toggle
将是
(toggle //) nil "foo"
{ 5 }
= (//) "foo" nil
{ 4 }
"foo"
另一个会再次相同:
foldl (toggle //) ((toggle //) nil "foo") (“bar” “baz”)
{ 1b }
= foldl (toggle //) ((toggle //) ((toggle //) nil "foo") "bar") ("baz")
我相信你可以解决剩下的问题(这只是把正确的东西放到正确的现场)
答案 1 :(得分:2)
我要做的第一件事是用Haskell语法和名称替换所有外来语法和名称。
toggle ⇒ flip
nil ⇒ []
(“foo” “bar” “baz”) ⇒ ["foo", "bar", "baz"]
(//) ⇒ (++)
,并提供:
foldl (toggle (//)) nil (“foo” “bar” “baz”)
⇒ foldl (flip (++)) [] ["foo", "bar", "baz"]
注意:我正在将
(toggle //)
更正为(toggle (//))
,因为我认为后者是预期的。前者是类型错误。
现在您只需扩展foldl
的定义。我再次用Haskell语法和名称替换外来语法和名称。
foldl f x NIL = x
foldl f x (cons a list) = foldl f (f x a) list
变为:
foldl f a [] = a
foldl f a (x:xs) = foldl f (f a x) xs
现在您将foldl
应用于其参数。你可以做几件事。有一点是重写foldl
所以它是一个表达式。
foldl =
\f -> \a -> \xxs ->
case xxs of
[] -> a
x:xs -> foldl f (f a x) xs
或者在一行上:
foldl = \f -> \a -> \xxs -> case xxs of [] -> a; x:xs -> foldl f (f a x) xs
您还可以将列表["foo", "bar", "baz"]
变为"foo":"bar":"baz":[]
,这使得模式匹配更加明显。
现在展开foldl
并应用:
foldl (flip (++)) [] ("foo":"bar":"baz":[])
⇒ (\f -> \a -> \xxs -> case xxs of [] -> a; x:xs -> foldl f (f a x) xs) (flip (++)) [] ("foo":"bar":"baz":[])
⇒ (\a -> \xxs -> case xxs of [] -> a; x:xs -> foldl (flip (++)) ((flip (++)) a x) xs) [] ("foo":"bar":"baz":[])
⇒ (\xxs -> case xxs of [] -> []; x:xs -> foldl (flip (++)) ((flip (++)) [] x) xs) ("foo":"bar":"baz":[])
⇒ case "foo":"bar":"baz":[] of [] -> []; x:xs -> foldl (flip (++)) ((flip (++)) [] x) xs
简化案例表达:
case ("foo":"bar":"baz":[]) of [] -> []; x:xs -> foldl (flip (++)) ((flip (++)) [] x) xs
⇒ foldl (flip (++)) ((flip (++)) [] "foo") ("bar":"baz":[])
您可以以相同的方式完成规范化。
一旦你理解了foldl
之类的函数定义在案例表达式上真的只是糖,你就可以采取捷径。
注意:还有其他丰富的语法,例如警卫和记号。当你想要评估它们时,它将有助于了解基础表达。
从:
开始foldl (flip (++)) [] ("foo":"bar":"baz":[])
我们可以看一下foldl
的定义:
foldl f a [] = a
foldl f a (x:xs) = foldl f (f a x) xs
然后立即根据哪个案例匹配选择a
定义或foldl f (f a x) xs
定义。在这种情况下,它是后者。选择了正确的定义后,我们将所有参数替换为参数,并快速得到与之前相同的表达式:
foldl (flip (++)) ((flip (++)) [] "foo") ("bar":"baz":[])
您可以看到我的一般方法是简化语法。也就是说,操作更简单而不是更简单的读取或写入,这通常意味着减少更少的原语。我们只使用case
,抽象(即\x -> m
)和应用程序(即f x
,用参数替换参数)进行管理。如果关于如何使用更丰富的语法(或者在这个问题,发明语法的情况下)的规则不明确,这是一个很好的方法。