我将一些代码移植到Haskell,广泛使用的概念 一个偷看的迭代器。它基本上包装了一个集合并提供了两个 功能," next"并且"偷看"。 "下一个"功能推进了 迭代器并返回head元素,而" peek"功能 返回head元素而不推进迭代器。会是什么 将这个概念翻译成Haskell的优雅方式?我最好的主意 远远是基本上使用国家monad来跟踪当前 迭代器的位置。有更清洁的方式吗?
(我在初学者@上发布了这个,但没有得到任何答案)
答案 0 :(得分:6)
副作用迭代器的想法与Haskell Way是对立的。你当然可以鞭打一个monad,但那时你正在使用Haskell(引用Simon PJ)世界上最好的命令式语言。
在不了解您尝试移植的代码的情况下,我无法提供非常具体的建议,但这是我的一般想法:
使用某种 fold 实现迭代。
传递给折叠操作的函数应该是其他函数的组合。可能是零或更多'偷看'操作的组合加上一个'nexting'操作。
如果您的折叠需要a -> b -> a
类型的内容,那么您的peek
和next
版本可能都具有此类型,并且您可以这样编写它们:
peek_then_next :: (a -> b -> a) -> (a -> b -> a) -> (a -> b -> a)
peek_then_next peek next = next'
where next' a b = let a' = peek a b
in next a' b
您会看到peek
和next
参数都看到相同的b
,但peek
会将信息累积到a'
中,然后next
通过foldl
操作看到。
您可以根据需要撰写尽可能多的内容,然后将作文传递给{{1}}或类似内容。
答案 1 :(得分:4)
type Iterator a = [a]
peek :: Iterator a -> a
peek = head
next :: Iterator a -> Iterator a
next = tail
你所描述的听起来像是Haskell常规的旧内置单链表。甚至可能是zipper。
答案 2 :(得分:3)
通常在Haskell中,您将计算无限列表(通常由递归函数生成),而不是迭代器。您只需迭代列表(通常使用递归函数,折叠或贴图等),而不是“从迭代器中获取元素”,而是根据需要查看列表中的元素。因为Haskell很懒,所以它只根据需要计算值。
我认为你应该使用带有无限列表的递归函数来实现你的任务。要“窥视”,只需查看列表的第一个元素即可。 (您可以根据需要“查看”尽可能多的元素,只需通过索引列表即可“查看”列表的当前“头部”。)要“推进”迭代器,只需使用列表的尾部递归调用自己。
答案 3 :(得分:1)
你可以在Haskell中做到你想要的,但要注意这些是重枪:
{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-}
class Iterator as a | as -> a where
peek :: as -> a
next :: as -> (as, a)
--sample implementation
instance Iterator [a] a where
peek as = head as
next as = (tail as, head as)
--sample use
main = print $ next $ [3,4]
--([4],3)
当然Iterator
没有改变,它必须回赠它的新版本(非常类似于随机数生成器),但我认为“可变”的东西在这里会有点过分。
那就是说,我认为你应该使用更多惯用的方法在Haskell中做你想做的事。也许你应该看看Reader
monad是如何工作的:http://learnyouahaskell.com/for-a-few-monads-more#reader