Haskell 哈斯克尔的教堂名单

Haskell 哈斯克尔的教堂名单,haskell,lambda-calculus,church-encoding,Haskell,Lambda Calculus,Church Encoding,我必须实现haskell映射函数来处理教堂列表,其定义如下: type Churchlist t u = (t->u->u)->u->u [] := λc. λn. n [1,2,3] := λc. λn. c 1 (c 2 (c 3 n)) 在lambda演算中,列表编码如下: type Churchlist t u = (t->u->u)->u->u [] := λc. λn. n [1,2,3] := λc. λn. c 1 (c 2

我必须实现haskell映射函数来处理教堂列表,其定义如下:

type Churchlist t u = (t->u->u)->u->u
[] := λc. λn. n
[1,2,3] := λc. λn. c 1 (c 2 (c 3 n))
在lambda演算中,列表编码如下:

type Churchlist t u = (t->u->u)->u->u
[] := λc. λn. n
[1,2,3] := λc. λn. c 1 (c 2 (c 3 n))
本练习的示例解决方案为:

mapChurch :: (t->s) -> (Churchlist t u) -> (Churchlist s u)
mapChurch f l = \c n -> l (c.f) n

我不知道这个解决方案是如何工作的,也不知道如何创建这样一个函数。我已经有了lambda微积分和church数词的经验,但是这个练习让我非常头疼,我必须在下周的考试中理解并解决这些问题。有人能给我一个好的来源,让我学习如何解决这些问题,或者给我一些指导吗?

因此,让我们从编码两个列表构造函数开始,使用您的示例作为参考:

[] := λc. λn. n
[1,2,3] := λc. λn. c 1 (c 2 (c 3 n))
[]
是列表末尾的构造函数,我们可以直接从示例中提取它<代码>[]在haskell中已经有了意义,所以我们称我们的
nil

nil = \c n -> n
mapChurch f nil
= \c n -> nil (c.f) n
= \c n -> (\c' n' -> n') (c.f) n
= \c n -> n
= nil
我们需要的另一个构造函数接受一个元素和一个现有列表,并创建一个新列表。通常,这被称为
cons
,其定义如下:

cons x xs = \c n -> c x (xs c n)
我们可以检查这是否与上面的示例一致,因为

cons 1 (cons 2 (cons 3 nil))) =
cons 1 (cons 2 (cons 3 (\c n -> n)) = 
cons 1 (cons 2 (\c n -> c 3 ((\c' n' -> n') c n))) =
cons 1 (cons 2 (\c n -> c 3 n)) =
cons 1 (\c n -> c 2 ((\c' n' -> c' 3 n') c n) ) =
cons 1 (\c n -> c 2 (c 3 n)) =
\c n -> c 1 ((\c' n' -> c' 2 (c' 3 n')) c n) =
\c n -> c 1 (c 2 (c 3 n)) =

现在,考虑MAP函数的目的,它是将给定函数应用到列表的每个元素。让我们看看这对每个构造函数是如何工作的

nil
没有元素,因此
mapchutch f nil
应该是
nil

nil = \c n -> n
mapChurch f nil
= \c n -> nil (c.f) n
= \c n -> (\c' n' -> n') (c.f) n
= \c n -> n
= nil
cons
有一个元素和列表的其余部分,因此,为了使
mapChurch f
正常工作,它必须将
f
应用于元素,将
mapChurch f
应用于列表的其余部分。也就是说,
mapChurch f(cons x xs)
应该与
cons(f x)(mapChurch f xs)
相同


因此,由于所有列表都是由这两个构造函数组成的,
mapChurch
在这两个构造函数上都能按预期工作,
mapChurch
必须在所有列表上按预期工作。

所有lambda演算数据结构都是函数,因为lambda演算中只有这些。这意味着布尔、元组、列表、数字或任何东西的表示必须是表示该事物活动行为的函数

对于列表,它是一个“折叠”。不可变单链表通常定义为
List a=Cons a(List a)| Nil
,这意味着构建列表的唯一方法是
Nil
Cons anElement anotherList
。如果以lisp风格编写,其中
c
Cons
n
Nil
,则列表
[1,2,3]
如下所示:

(c 1 (c 2 (c 3 n)))
map :: (a -> b) -> [a] -> [b]
map f xs = foldr (\x xs' -> f x:xs') [] xs
执行列表折叠时,只需提供自己的“
Cons
”和“
Nil
”来替换列表中的内容。在Haskell中,此函数的库函数是
foldr

foldr :: (a -> b -> b) -> b -> [a] -> b
看起来眼熟吗?取出
[a]
,您就拥有了与
列表a b完全相同的类型。正如我所说,church编码将列表表示为它们的折叠函数


因此,示例定义了
map
。请注意
l
是如何用作函数的:毕竟,它是一个可以折叠某些列表的函数<代码>\c n->l(c.f)n
基本上是说“用
c.f
替换每个
c
,用
n
替换每个
n


现在应该很明显,这确实是一个映射函数,因为它看起来与原始函数一样,除了
1
变成
(f1)
2
(f2)
,以及
3
(f3)
之外,我们可以用这种方式对教堂名单类型进行注释以澄清它:

-- Tell me...
type Churchlist t u = (t -> u -> u) -- ...how to handle a pair
                    -> u            -- ...and how to handle an empty list
                    -> u            -- ...and then I'll transform a list into 
                                    -- the type you want
请注意,这与
foldr
功能密切相关:

foldr :: (t -> u -> u) -> u -> [t] -> u
foldr k z [] = z
foldr k z (x:xs) = k x (foldr k z xs)
foldr
是一个非常通用的函数,可以实现各种其他列表函数。一个可以帮助您的简单示例是使用
foldr
实现列表副本:

copyList :: [t] -> [t]
copyList xs = foldr (:) [] xs
使用上面的注释类型,
foldr(:)[]
的意思是:“如果看到空列表,返回空列表,如果看到一对返回
head:tailResult

使用
Churchlist
,您可以通过以下方式轻松编写对应项:

-- Note that the definitions of nil and cons mirror the two foldr equations!
nil :: Churchlist t u
nil = \k z -> z

cons :: t -> Churchlist t u -> Churchlist t u
cons x xs = \k z -> k x (xs k z)  

copyChurchlist :: ChurchList t u -> Churchlist t u
copyChurchlist xs = xs cons nil
现在,要实现
map
,您只需要用合适的函数替换
cons
,如下所示:

(c 1 (c 2 (c 3 n)))
map :: (a -> b) -> [a] -> [b]
map f xs = foldr (\x xs' -> f x:xs') [] xs
映射就像复制一个列表,不同的是不只是逐字复制元素,而是将
f
应用到每个元素

仔细研究所有这些,你应该能够自己编写
mapChurchlist::(t->t')->churchlisttu->churchlisttu

额外练习(简单):根据
foldr
编写这些列表函数,并为
Churchlist
编写相应的函数:

filter :: (a -> Bool) -> [a] -> [a]
append :: [a] -> [a] -> [a]

-- Return first element of list that satisfies predicate, or Nothing
find :: (a -> Bool) -> [a] -> Maybe a

如果你想处理更难的事情,试着为
教堂列表写
tail
。(首先用
foldr
[a]
tail

这个解释真是太棒了!谢谢。你救了我一天,维基百科上的页面似乎是一个很好的起点。@jcdmb:你在KIT学习计算机科学吗?