Haskell 哈斯克尔:懒惰的评估能帮助提前停止投票吗?
假设我有10个不同的函数(并行或非并行)决定同一个问题。 有没有一个好方法可以实现一个投票方案,当达到多数时,lazy会自动实现,而不需要更多的计算 obs:这更多的是关于懒惰ev的范围/限制的问题。 当然,一个简单的“如果”就可以检测出多数 谢谢 [编辑1] 。。。简单的“如果”可以检测到多数 对不起,我的意思是“单一如果”->“单一等待所有进程完成” 。。。平行与否Haskell 哈斯克尔:懒惰的评估能帮助提前停止投票吗?,haskell,lazy-evaluation,Haskell,Lazy Evaluation,假设我有10个不同的函数(并行或非并行)决定同一个问题。 有没有一个好方法可以实现一个投票方案,当达到多数时,lazy会自动实现,而不需要更多的计算 obs:这更多的是关于懒惰ev的范围/限制的问题。 当然,一个简单的“如果”就可以检测出多数 谢谢 [编辑1] 。。。简单的“如果”可以检测到多数 对不起,我的意思是“单一如果”->“单一等待所有进程完成” 。。。平行与否 我只是不知道并行性在这种情况下很重要。如果我认为这些函数产生布尔值,问题就变成了,如果写出一个函数,它需要10个布尔值,如果6
我只是不知道并行性在这种情况下很重要。如果我认为这些函数产生布尔值,问题就变成了,如果写出一个函数,它需要10个布尔值,如果6个是真的,则总是返回真值,它总是需要小于其输入的10的值。p>
一种简单但不符合规定要求的方法是,依次测试每个输入,如果trues>=6停止返回true,则计数trues和false的数量;如果false>=6停止返回false,则计数trues和false的数量;如果我们到达最后一个输入时未触发任何一个条件,则返回false。由于这将在某些情况下测试所有输入,因此我认为这个问题的答案是否定的,在本例中,延迟求值没有帮助。您想要这样的函数:
majority :: [Bool] -> Bool
voteByTrue list = sum (map bToNat list) >= threshold
where
threshold = (genericLength list + 1) `quot` 2
你想让它并行工作。举手之劳不幸的是,我不知道一种不绕过类型系统的方法。下面是一个示例实现:
import Control.Concurrent
import Control.Concurrent.MVar
import System.IO.Unsafe
majority :: [Bool] -> Bool
majority votes = unsafePerformIO $
do v <- newEmptyMVar
nfalse <- newMVar 0
ntrue <- newMVar 0
let n = length votes
m = (n `div` 2) + 1
count x =
let (var, min) = if x then (ntrue, m) else (nfalse, n-m+1)
in do i <- modifyMVar var $ \i -> return (i+1, i+1)
if i == min then putMVar v x else return ()
threads <- mapM (forkIO . count) votes
r <- takeMVar v
mapM_ killThread threads
return r
导入控制。并发
导入控制.Concurrent.MVar
导入System.IO不安全
多数::[Bool]->Bool
多数票=不安全$
做一个简短的回答。是的,实施这样一个系统是可能的,但是不,内在的懒惰对你没有帮助
答案很长。我认为你需要一点不同的懒惰。Haskell的惰性评估是一种,其工作原理如下:
调用该函数时,evaluator尝试首先计算它,而不计算其参数李>
如果控制流到达需要计算某个参数的点,它将对其求值。然后继续对函数进行评估李>
因此,参数会根据需要“按需”进行评估。并对其逐一进行了评价。对于语言本身来说,这是一个好主意,即使是具有应用程序求值顺序的命令式语言,如果没有这样的惰性函数也无法工作-在大多数编程语言中,像或和和这样的操作符本质上是惰性的。但在你的情况下,这是你真正需要的吗?不。您需要并行计算所有参数,并在计算某些参数时完成函数本身的计算
如何实现。您需要完全重新实现评估系统,我相信没有副作用的纯函数式编程和懒惰的评估只会阻碍您。这里有一个方法。创建函数,例如,paply::[ArgumentType]->TriggerFunction->ResultType
,其中pappy
代表“并行应用”,ArgumentType
是要计算的实际参数类型(在您的情况下可能是函数的闭包+要解决的问题),TriggerFunction
是一个函数,在计算其中一个参数时调用,并且在您的示例中,ResultType
是布尔值。此功能必须按如下方式工作:
并行运行所有参数的计算
当计算其中一个参数时,它必须使用计算结果调用Trigger函数李>
触发器函数必须有一个“内存”来记住所有以前的结果。如果在调用时,它发现有足够的参数来完成对主函数的求值,它就会这样做,从而中断对其余参数的计算李>
这只是其中一种方法,不是最实用的(它使用可变的“内存”)。您还可以与其他参数并行运行触发器函数,并使用某种同步在所有参数之间传递控制。或者您可以使用诸如Erlang或Scala中的某种消息。不幸的是,我没有足够的Haskell经验来编写实际的代码,但是@Dietrich Epp的帖子似乎代表了类似的想法,所以您可以将其作为基础 您可以使用懒惰的自然语言来完成这项工作,而无需进行简单的并行计算。在本例中,我选择在hackage上使用peano inf包:
请注意,5不会打印在跟踪中,因为在此之前会缩短评估
要使用并行性实现这一点,需要手动生成和终止线程等,这很好,但肯定不那么令人愉快
请注意,上述代码使用标准和。这种不常见的用例就是为什么,尽管很多人觉得它不值得,但sum没有尽可能严格。我尝试将sclv的解决方案与luqui关于unab
的评论结合起来,并想分享我的结果。我将从测试用例开始:
list1 = [True, True, undefined, True, undefined]
list2 = [undefined, False, False]
list3 = concat $ replicate 500 list1
list4 = concat $ replicate 500 list2
main = mapM (print . vote) [list1, list2, list3, list4]
vote :: [Bool] -> Bool
这应该打印出来
True
False
True
False
我首先从list1
示例开始。通过它的投票函数可以如下所示:
majority :: [Bool] -> Bool
voteByTrue list = sum (map bToNat list) >= threshold
where
threshold = (genericLength list + 1) `quot` 2
这与sclv的答案相同。现在我们需要使sum
更延迟,以便在遇到未定义的summand时计算不会中止。我的第一个想法是:
Zero |+ y = y
Succ x |+ y = Succ (x + y)
instance Num Nat where
x + y = (x |+ y) `lub` (y |+ x)
这里,|+
在其第一个参数中是严格的加法运算符,+
在其两个参数中都是非严格的。它适用于玩具示例,如list1
,但由于线程数呈指数级增长,该解决方案的性能会迅速恶化(请参见每个+
如何生成2个线程,每个线程通常使用相同的参数再次调用+
)。有了这样的表现,投票列表3
的终止速度不够快。为了对抗这一点,我
instance Num Nat where
x + y = x' |+ y' where (y', x') = infoMinMax x y
vote list = voteByTrue list `maxInfo` voteByFalse list
where
voteByFalse = not . voteByTrue . map not
maxInfo x y = snd (infoMinMax x y)