在Haskell的编程中,Graham Hutton定义了列表展开如下:
unfold :: (b -> Bool ) -> (b -> a) -> (b -> b) -> b -> [a]
unfold p h t x | p x = []
| otherwise = h x : unfold p h t (t x)
定义一个函数
• listUnfold :: (b -> Bool) -> (b -> a) -> (b -> b) -> b -> [a]
与上面的类似,但在其实现中使用unfoldr并且是非递归的。
我已经尝试了一段时间来解决上面的问题,但我仍然可以设法这样做(在Haskell和一般的函数式编程中相当新)。
我的尝试:
listUnfold :: (b -> Bool) -> (b -> a) -> (b -> b) -> b -> [a]
listUnfold f h t x
| f x == True = []
| otherwise = h x :
listUnfoldr (\x -> if f x then Nothing else Just ((h x), (t x))) x
在英语中,如果f x
为真,则返回空列表。否则,使用h x
作为头部并将展开的结果作为尾部附加。 Unfoldr获取列表(x:xs)
应该以{{1}}为头,x
为尾部进行递归。
答案 0 :(得分:6)
你差不多了!原始函数使用函数p
(对于'谓词')来确定我们是否已完成展开,h
以应用于每个元素,以及t
(对于'转换')将一个元素转换为列表其余部分的种子。
unfoldr
需要单个函数f :: b -> Maybe (a,b)
,如果我们已完成展开则返回Nothing
,或Just (x, y)
,其中x
是要添加的元素到列表中,y
是列表其余部分的种子。
因此f
中的unfoldr
负责p
,h
和t
的所有三个功能。 Nothing
- 或 - Just
二分法扮演布尔函数p
的一部分,元组的第二个元素执行t
为种子提供种子的工作列表的其余部分。
这是我的解决方案(为了清楚起见,我已将您问题中的变量重命名):
listUnfold pred f trans seed =
unfoldr (\x -> if pred x
then Nothing
else Just (f x, trans x)) seed
当然,当一个值出现在定义的右端时,正如seed
在这里所做的那样,你可以利用Haskell的性感语法进行调整并完全抛弃:
listUnfold pred f trans = unfoldr (\x -> if pred x
then Nothing
else Just (f x, trans x))
形式上,这种转变称为eta reduction。
答案 1 :(得分:2)
unfoldr :: (b -> Maybe (a, b)) -> b -> [a]
unfoldr f b =
case f b of
Just (a,new_b) -> a : unfoldr f new_b
Nothing -> []
这是Hutton的unfold
,稍微改写一下,使用case
代替守卫:
unfold :: (b -> Bool ) -> (b -> a) -> (b -> b) -> b -> [a]
unfold p h t x =
case p x of
True -> []
False -> h x : unfold p h t (t x)
一些观察结果:
unfoldr
和unfold
共享相同的最终参数类型和相同的结果类型。
b
在unfoldr
的定义中x
和unfold
的定义基本上是同一个变量。case
表达式,带有两个分支。[]
。
p x = True
时,我们需要f x = Nothing
。{-something-} : {-recursive-call-}
。
p x = False
时,我们需要f x = Just ({-something-}, {-something-else-})
。到目前为止,unfold p h t x = unfoldr f x where f = ...
应该很清楚,并且进行推理链并填写f
的定义并不太难。