- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在尝试找到将以下有状态命令式代码转换为纯函数表示的最优雅的方法(最好在 Haskell 中使用其 Monad 实现提供的抽象)。然而,我还不擅长使用变压器等组合不同的单子(monad)。在我看来,在学习如何自己完成此类任务时,分析他人对此类任务的看法最有帮助。命令式代码:
while (true) {
while (x = get()) { // Think of this as returning Maybe something
put1(x) // may exit and present some failure representation
}
put2() // may exit and present some success representation
}
何时 get
返回Nothing
我们需要继续执行 put2
,当 get
返回Just x
我们想要 x
传递给put1
并且仅当 put1
时才短路否则失败或循环。基本上put1
和put2
可能会终止整个事情或移动到以下语句以某种方式改变底层状态。 get
可以成功并调用 put1
并循环或失败并继续put2
.
我的想法是这样的:
forever $ do
forever (get >>= put1)
put2
我为什么要寻找类似的东西是因为 (get >>= put1)
只要 get
就可以简单地短路没有任何可返回的内容或 put1
终止。同样put2
终止外循环。但是我不知道如何混合 State
与必要的Maybe
和/或 Either
来实现这一目标。
我认为使用变压器来组合 State
而其他 monad 是必要的,因此代码很可能不会那么简洁。但我想情况也可能不会更糟。
欢迎提出如何优雅地实现翻译的任何建议。这与“Stateful loop with different types of breaks ”不同,避免使用 if
显式控制流。 , when
, while
而是试图鼓励使用 Maybe
, Either
,或其他一些方便的>>=
语义。此外,总有一种直接的方法可以将代码转换为功能性代码,但它很难被认为是优雅的。
最佳答案
您正在寻找 EitherT
或 ExceptT
。它添加了两种返回变压器堆栈的方法。计算可以是 return a
或throwError e
。错误和返回之间有两个区别。错误保留在 Left
上并返回 Right
。当你 >>=
出现错误时会短路。
newtype EitherT e m a = EitherT { runEitherT :: m (Either e a) }
return :: a -> EitherT e m a
return a = EitherT $ return (Right a)
throwError :: e -> EitherT e m a
throwError e = EitherT $ return (Left a)
我们还将使用名称 left = throwError
和right = return
.
Left
上的错误不要继续,我们将使用它们来表示退出循环。我们将使用类型EitherT r m ()
表示一个循环,该循环要么因破坏结果而停止 Left r
或继续 Right ()
。这几乎完全是forever
,除非我们打开 EitherT
并摆脱 Left
围绕返回值。
import Control.Monad
import Control.Monad.Trans.Either
untilLeft :: Monad m => EitherT r m () -> m r
untilLeft = liftM (either id id) . runEitherT . forever
在充实您的示例后,我们将回到如何使用这些循环。
由于您希望看到几乎所有逻辑都消失,因此我们将使用 EitherT
对于其他一切也是如此。获取数据的计算是 Done
或返回数据。
import Control.Monad.Trans.Class
import Control.Monad.Trans.State
data Done = Done deriving Show
-- Gets numbers for a while.
get1 :: EitherT Done (State Int) Int
get1 = do
x <- lift get
lift . put $ x + 1
if x `mod` 3 == 0
then left Done
else right x
放置数据的第一个计算是 Failure
或返回。
data Failure = Failure deriving Show
put1 :: Int -> EitherT Failure (State Int) ()
put1 x = if x `mod` 16 == 0
then left Failure
else right ()
放置数据的第二个计算是 Success
或返回。
data Success = Success deriving Show
put2 :: EitherT Success (State Int) ()
put2 = do
x <- lift get
if x `mod` 25 == 0
then left Success
else right ()
对于您的示例,我们需要组合两个或多个计算,这些计算都以不同的方式异常停止。我们将用两个嵌套 EitherT
来表示这一点s。
EitherT o (EitherT i m) r
外层EitherT
是我们目前正在运营的一个。我们可以转换 EitherT o m a
到EitherT o (EitherT i m) a
通过添加额外的 EitherT
每一层 m
†。
over :: (MonadTrans t, Monad m) => EitherT e m a -> EitherT e (t m) a
over = mapEitherT lift
内在EitherT
层将像变压器堆栈中的任何其他底层单子(monad)一样被对待。我们可以lift
一个EitherT i m a
到EitherT o (EitherT i m) a
我们现在可以构建一个要么成功要么失败的整体计算。会破坏当前循环的计算被操作 over
。会破坏外循环的计算是 lift
编辑。
example :: EitherT Failure (State Int) Success
example =
untilLeft $ do
lift . untilLeft $ over get1 >>= lift . put1
over put2
总体Failure
是 lift
ed 两次进入最内循环。这个例子很有趣,可以看到一些不同的结果。
main = print . map (runState $ runEitherT example) $ [1..30]
<小时/>
†如果 EitherT
有一个 MFunctor
例如,over
只是 hoist lift
,这是一种经常使用的模式,值得拥有一个经过深思熟虑的名字。顺便说一句,我用EitherT
超过ExceptT
主要是因为它的名称加载较少。无论哪一个提供 MFunctor
对我来说,实例首先将最终胜出作为任一 monad 转换器。
关于haskell - 具有不同类型短路的状态计算(也许,或者),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32398321/
我做了一些研究,但没有找到任何东西。我不明白这样的函数是如何工作的: func :: Maybe (Int) -> Maybe (Int) 我应该如何进行模式匹配?我已经尝试过,但没有成功: func
我需要从屏幕上删除一个元素,然后重新生成一个具有相同名称的元素。 代码中有一个deleteObject函数和一个appendChild调用。 在deleteObject函数中,它使用removeChi
我读了一些关于 monad 的帖子和博客,也许,只是,什么都没有..但并没有真正明白:/在给定的代码中,我必须实现“latestActivity”函数。在我看来,它应该有效,但我不知道如何正确使用“J
在功能光学中,一个性能良好的棱镜(我相信在 scala 中称为部分透镜)应该具有 'subpart -> 'parent -> 类型的 set 函数'parent,如果棱镜“成功”并且在结构上与给定的
有输入文件;未排序的长类型数字。(约100万)我想对输入文件中的数字进行排序。为了分配数组的内存,我使用了fseek和ftell。但出现段错误。如何修复代码? int main( int argc,
我仍在尝试从单元格收集信息,使用该信息执行函数,然后将结果返回到不同的单元格。我知道这是可能的,但我正在努力解决这个问题。我在这里收到的许多提示引导我找到了以我理解的方式表达的大量信息。我希望这种事能
所以我正在制作一个小型命令行彩票游戏(代码中的注释解释了一切),但是当我生成随机数时,我的代码并没有像我希望的那样将其缩短为三位数,除了游戏可以运行并且可以玩之外,除了偶尔有一个随机生成的数字超过一千
这是另一种情况,在 C++ 中空格很重要,还是编译器错误?以下代码在语法上是否正确? #include template using EnableIf = typename std::enable
我参加了一个 PHP 工作面试,我被要求实现一段代码来检测访问者是否是爬行网站并窃取内容的机器人。 因此,我实现了几行代码,通过使用 session 变量存储上次访问时间戳来检测网站是否刷新/访问过快
我有一个List (Maybe a),我想过滤出Nothing的实例。我大概已经做到了,但是对所需的代码量却不满意: removeNothingFromList : List (Maybe a) ->
有谁知道任何指定场所开放时间的本体?例如,我有一个博物馆,有 2 个季节。淡季(指定季节起止),平日10-18:00,周六10-16(周日休息),旺季平日10-20,周末10-18。 如果没有本体,也
我有this代码(接受的解决方案)。此代码从 js 文件中截取加载。当我在此函数处放置断点时,我看到该函数在加载页面(包含它)时被调用。 初始页面加载后,当我在此页面中选择一个选项时,该 anchor
step :: [Int] -> String -> [Int] step (x:y:ys) "*" = (x*y):ys step (x:y:ys) "+" = (x + y):ys step (x
我正在尝试找到将以下有状态命令式代码转换为纯函数表示的最优雅的方法(最好在 Haskell 中使用其 Monad 实现提供的抽象)。然而,我还不擅长使用变压器等组合不同的单子(monad)。在我看来,
我希望它更方便地使用库定义的partialfunc,或者使用部分模式匹配编写回调。 像这样, partialMaybe :: forall a b. (Partial => a -> b) -> a
一周前,我在代码中编写了一个名为 getline 的函数,但该函数不起作用。从那时起,每当我将函数命名为 getline 并尝试编译它时,它都不起作用。如果我将函数名称更改为其他名称,它会再次起作用。
下面的代码有问题 package com.example.ch13_searchflickrr; import android.os.Bundle; import android.support.v7
我有这个需求,并且我使用的是MySQl数据库 评论表 id user id ariticle_id comment comment date ================
是否有 IDE 或软件可以为我的 C++ 程序计时?我目前正在使用 Visual Studio 2010,所以如果有功能可以帮助解决这个问题,我将不胜感激。 最佳答案 您将需要使用 Profiler。
我是 C# 的新手,正在研究它的可能性。 现在我对使用泛型的方式有点困惑...列出泛型的种类。我想在单个父类中创建一个基本的列表功能,只需命名我的子类应包含的类类型。 比如说,我创建了一个类 clas
我是一名优秀的程序员,十分优秀!