首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >用任一方法中断递归

用任一方法中断递归
EN

Stack Overflow用户
提问于 2021-05-16 14:47:09
回答 2查看 192关注 0票数 2

在我的项目中,我在几个地方使用了以下unfold函数。

代码语言:javascript
复制
unfold :: (a -> Either b a) -> a -> b
unfold f x = either id (unfold f) $ f x

这似乎是一种非常普遍的递归模式(只需在a类型的值上应用一个函数,直到得到一个Right b),但我在Hoogle的某个地方找不到这样的函数。

它真的没有在任何地方定义吗?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2021-05-16 14:58:42

此函数在额外的包中作为Control.Monad.Extra.loop实现。

循环:(a ->或a b) -> a -> b 循环操作,其中谓词返回左边作为下一个循环的种子或右侧中止该循环。 循环(\x ->,如果x< 10,则左转$x*2右$ show x) 1 == "16“

额外的包还提供了一元版本的loopM

作为未来的注意,除了按名称搜索Hoogle函数外,您可以简单地插入类型签名,Hoogle将查找与此签名匹配的函数。我通过将(a -> Either a b) -> a -> b输入Hoogle 正因如此找到了上面的函数。

票数 2
EN

Stack Overflow用户

发布于 2021-05-16 16:21:01

如果您想要从extra版本中翻转类型参数的版本,那么单环包中也有iterateM_

代码语言:javascript
复制
iterateM_ :: Monad m => (a -> m a) -> a -> m b

这并不能一步解决问题,但它更准确地捕获了递归的底层模式。

请注意,这里没有针对Either的特定内容,这确实使它更难找到。我是通过胡格找到的。我不知道它是否存在,但我知道如果有这样的名字存在,它会解决你的问题。我是从iterate in base开始的。它重复地将一个函数应用到上一步的输出,并带有一个初始种子值,就像您想要的那样。但你想要在任何时候都能短路,Either的monad实例就是这样,所以像iterateM这样的名字会更有意义。除非你不关心所有的中间结果,所以iterateM_才是有意义的名字。

(而且,所有产生中间结果的东西都需要一个流系统来完成。这在回顾中是有意义的,但解释了为什么hoogle改变了它在最后添加"_“时显示的结果包。)

现在,就这实际上不是您想要的类型而言:是的,当它变得更通用时,有一点丢失了。但这可以用一种有趣的方式来恢复。回顾一下iterateM_的类型,注意返回值是m b,并且在前面的类型中没有提到b。这是一个微妙但意义重大的信息。因为函数不可能仅仅组成一个它不知道的多态类型的值,这意味着它根本不能产生这样的值。让我们在类型中内联Either,看看会发生什么:iterateM_ :: (a -> Either r a) -> a -> Either r b。我们如何安全地将Either r b转换为r?嗯,我们知道我们可以为b选择任何东西,因为它不可能存在。幸运的是,有一些工具可以解决这个问题。

base包含一个模块Data.Void,它在这里很有帮助。它有一个没有构造函数的类型Void。你可以认为它是一个类型级别的意符,某些事情不可能发生。由于这是不可能发生的,所以有一个适配器可以根据需要在值级别上将事情结合在一起:absurd :: Void -> a。这个名字来源于逻辑,基于这样的理念:一旦不可能发生,当你处于一个愚蠢的情况下,也许还会允许其他任何事情发生。在操作级别上,您可以在Haskell中具有Void类型的值,因为undefined :: Void将进行类型检查。但是absurd是完全安全的--它强制对其论点进行评估。由于任何时候它实际上是评估的参数必须是一个底部的某种类型的值,这仍然是类型安全。

那么,这如何与iterateM_相适应呢?好吧,事情已经解决了,所以我们知道有一个Either r b值必须是Left构造函数,但是使用fromLeft感觉很脏。但有趣的是,虽然r受到上下文的限制,但b仍然是完全多态的。我们可以为它选择我们想要的任何类型,因为我们知道它永远不会发生。所以选择Void,给either id absurd :: Either r Void -> r

代码语言:javascript
复制
unfold :: (a -> Either b a) -> a -> b
unfold f x = either id absurd $ iterateM_ f x

也许这比你的原版还不算进步。也许是这样--递归是在组合器中捕获的,而不是显式的。值得注意的是,该组合器捕获的递归模式比特定于Either的模式更为普遍。但是,这需要额外的转换步骤来重新调整之后的类型,这个步骤最终涉及到一个以前没有必要的新想法。在好的方面,它为如何使用类型系统来清楚地交流事物做了一个很好的说明。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/67558012

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档