Haskell foldr reduction

时间:2016-05-01 14:14:45

标签: haskell lambda fold reduction

我仍然是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”)

我知道这不对,但我觉得我在附近。如果我能得到一些指导,我觉得我可能会失去理解并获得成功的理解。

2 个答案:

答案 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,用参数替换参数)进行管理。如果关于如何使用更丰富的语法(或者在这个问题,发明语法的情况下)的规则不明确,这是一个很好的方法。