Purescript 中遍历/绑定/折叠绑定效果之间的差异

如何解决Purescript 中遍历/绑定/折叠绑定效果之间的差异

我一直在努力解决这个问题,我编写了四个我希望运行相同的函数,我很好奇它们为什么不同。

git checkout HEAD~2 "Finished 1 stage"

对于前两个,控制台似乎会在发生时显示每个效果。日志语句之间可能有 1/2 秒以上的延迟。我会非常惊讶地看到这些行为有所不同,因为我知道 main2 中的 do 符号只是 toEffect :: Tuple Int String -> Effect Unit toEffect (Tuple i strng) = log $ append (show i <> ": ") $ statefulPuzzletoString $ selectFirstLadderBruteForce $ parsePuzzle strng main1 :: Effect Unit main1 = (toEffect $ Tuple 1 $ fromMaybe "" $ hardestBoardStringsX11 !! 0) >>= (\_ -> toEffect $ Tuple 2 $ fromMaybe "" $ hardestBoardStringsX11 !! 1) >>= (\_ -> toEffect $ Tuple 3 $ fromMaybe "" $ hardestBoardStringsX11 !! 2) >>= (\_ -> toEffect $ Tuple 4 $ fromMaybe "" $ hardestBoardStringsX11 !! 3) -- ... Pattern Could continue for all 11 boards main2 :: Effect Unit main2 = do toEffect $ Tuple 1 $ fromMaybe "" $ hardestBoardStringsX11 !! 0 toEffect $ Tuple 2 $ fromMaybe "" $ hardestBoardStringsX11 !! 1 toEffect $ Tuple 3 $ fromMaybe "" $ hardestBoardStringsX11 !! 2 toEffect $ Tuple 4 $ fromMaybe "" $ hardestBoardStringsX11 !! 3 -- ... Pattern Could continue for all 11 boards main3 :: Effect Unit main3 = foldl (\acc new -> acc >>= \_ -> new) (pure unit) effects where effects :: Array (Effect Unit) effects = map toEffect $ mapWithIndex Tuple hardestBoardStringsX11 main4 :: Effect Unit main4 = traverse_ toEffect $ mapWithIndex Tuple hardestBoardStringsX11

中所写内容的语法糖

后两个同时出现记录他们的陈述。

我对 main1 并不完全确定,但我非常有信心 main4 的行为确实与前两个相同。

对这里发生的事情有任何了解吗?

解决方法

main3main4 的行为都是出于同样的原因,原因在于 evaluationexecution 之间的区别。 >


当您有一个 Effect a 类型的值时,它表示产生 a 的某种效果,大概是您从某个地方获得了该值。让我们说:

myEffect = makeMeAnEffect "foo"

此值已在 makeMeAnEffect求值,但尚未执行。在这里,“评估”意味着进行任何必要的计算以产生类型 Effect a 的值。创建此值可能涉及一些计算 - 例如数字相乘、字符串遍历、矩阵相加。这就是所有的“评估”。

但是评估的结果是对执行效果时应该发生什么的“描述”。这里“执行”的意思是“运行”效果,让它发生任何它描述的动作。

评估和执行在技术上是独立的概念。许多语言将它们混为一谈,但纯函数式语言,例如 PureScript 和 Haskell,保持严格的分离:首先创建应该发生什么的描述(“评估”),然后“运行”该描述(“执行”)。

这种区别在实践中非常重要:“评估”是纯粹的,这意味着除了结果之外它是完全不可观察的,因此编译器可以对它做任何想做的事情 - 例如优化、滚动/展开,甚至完全丢弃——只要结果保持不变。另一方面,“执行”必须按照程序员指定的确切方式执行,因为它的全部意义在于产生效果,因此弄乱它会产生明显的后果。


在您的特定情况下,在 toEffect 的主体中,评估是 log $ 之后发生的一切。所有对 appendselectFirstLadderBruteForce 等的调用 - 所有这些都是“评估”。这些都没有效果。您正在执行一些计算,以确定您将创建什么样的效果。

然后,一旦你完成了所有的计算,你将它的结果传递给 log,这使你成为一个 Effect Unit,它是“应该发生什么的描述”。在这种特殊情况下,“应该发生什么”非常小 - 只需向控制台写入一个字符串即可。


现在,我们终于可以了解 main1/main2main3/main4 之间的区别了。

main1main2 中,您仅在第一个效果执行后创建每个下一个效果。所以评估和执行“重叠”,可以这么说:首先你做第一个评估,创建第一个效果,然后运行它,然后,只有在它运行完成后,你才能进行第二个评估并创建第二个效果。等等。由于昂贵的部分(在您的情况下)是评估,因此每次下一次执行最终都会延迟下一次评估所需的时间。

另一方面,在 main3main4 中,您首先进行评估,通过在数组上调用 map toEffect 一次创建所有效果,然后才继续执行它们逐个。而且,由于再次评估(在您的情况下)是昂贵的部分,并且所有这些都在开始时发生,因此执行不会延迟。每个效果都非常小(只需打印到控制台),因此它们都执行得非常快。


如果你真的想在上一次执行完成之前阻止下一次评估发生,你可以使用这个技巧:在 pure unit 的开头添加一个 toEffect 像这样:

toEffect (Tuple i strng) = do
  pure unit

  log $ append (show i <> ": ") $
  statefulPuzzleToString $ 
  selectFirstLadderBruteForce $
  parsePuzzle strng

这将确保第二行在第一行执行之前不会开始评估,从而使每个评估只在其各自的执行之前发生。


最后,另一个有趣的事实:在 Haskell 中,相同的程序会以不同的方式工作,因为 Haskell 是懒惰的。当被要求进行评估时,它不会立即进行评估,而只是“记住”它已被要求进行评估。并且只有当评估的结果实际上是必要的(这会在执行时发生)时,才会执行。

另一方面,PureScript 是严格的,这意味着它总是立即计算所有内容。在这种特殊情况下,这意味着它会在将结果传递给 append 之前计算整个 log 等系列调用。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?