在Haskell中实现窥视迭代器

在Haskell中实现窥视迭代器,haskell,Haskell,我正在向Haskell移植一些广泛使用 偷窥迭代器。它本质上包装了一个集合,并提供了两个 功能,“下一步”和“偷看”。“下一步”功能使 迭代器并返回head元素,而“peek”函数 返回head元素而不推进迭代器。会是什么 把这个概念翻译成Haskell的优雅方式?我最好的主意是这样 far基本上是使用状态单子来跟踪当前状态 迭代器的位置。有没有更干净的方法 (我也在初学者@上发布了这篇文章,但没有得到任何答案)副作用迭代器的想法与Haskell方法相反。你当然可以设计一个monad,但在这一点

我正在向Haskell移植一些广泛使用 偷窥迭代器。它本质上包装了一个集合,并提供了两个 功能,“下一步”和“偷看”。“下一步”功能使 迭代器并返回head元素,而“peek”函数 返回head元素而不推进迭代器。会是什么 把这个概念翻译成Haskell的优雅方式?我最好的主意是这样 far基本上是使用状态单子来跟踪当前状态 迭代器的位置。有没有更干净的方法


(我也在初学者@上发布了这篇文章,但没有得到任何答案)

副作用迭代器的想法与Haskell方法相反。你当然可以设计一个monad,但在这一点上,你使用Haskell作为(引用simonpj的话)世界上最好的命令式语言

在不了解更多您尝试移植的代码的情况下,我无法给出非常具体的建议,但以下是我的一般想法:

  • 使用某种折叠实现您的迭代

  • 传递给折叠操作的函数应由其他函数组成。可能由零个或多个“偷窥”操作加上一个“下一步”操作组成

如果您的折叠需要类型为
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
或类似的内容。

通常在Haskell中,您可以计算无限列表(通常由递归函数生成),而不是迭代器。您不需要从迭代器中“获取元素”,只需沿着列表进行迭代(通常使用递归函数、折叠或映射等),然后查看列表中的元素。因为Haskell是懒惰的,所以它只在您需要时计算值

我认为您应该使用接受无限列表的递归函数来实现您的任务。要“偷看”,只需查看列表的第一个元素。(只要对列表进行索引,就可以根据需要“窥视”列表当前“头”后面的任意多个元素。)要“推进”迭代器,只需使用列表的尾部递归调用自己

type Iterator a = [a]

peek :: Iterator a -> a
peek = head

next :: Iterator a -> Iterator a
next = tail

您所描述的内容听起来像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)
当然,
迭代器
不会改变,它必须返回它的新版本(非常类似于随机数生成器),但我认为一些“可变”的东西在这里会被过度使用


也就是说,我认为在Haskell中,你真的应该使用更惯用的方式来做你想做的事情。也许你应该看看
阅读器
monad是如何工作的:

你看过Data.Enumerator包了吗。它附带了一个peek函数@ErikHinton-鉴于问题描述,枚举数可能是极端的过度使用。