假设我有10个重度不同的函数(并行与否)决定相同的问题。 是否有一种很好的方法来实现一种投票方案,当达到多数并且不再需要计算时,它会自动实现懒惰?
obs:这是关于懒惰ev的范围/限制的更多问题。 当然,简单的“if”可以检测到多数。
由于
[编辑1]
......简单的“if”可以检测到多数。
抱歉,我的意思是“单身如果” - > “单身等待所有过程完成”。
......平行与否......
在这种情况下,我只是不知道并行性问题。 (我模棱两可的英语问题)
答案 0 :(得分:4)
简短回答。是的,可以实现这样的系统,但不,内置的懒惰对你没有帮助。
答案很长。我相信你需要一点不同的懒惰。 Haskell的懒惰评估是一种normal evaluation order,其工作原理如下:
因此,根据需要评估参数,“按需”。而且,它们被逐一评估。对于语言本身来说这是一个好主意,即使没有这些惰性函数,具有应用评估顺序的命令式语言也无法工作 - 像or
和and
之类的运算符在大多数编程语言中都是懒惰的。但在你的情况下,你真的需要它吗?不。您需要并行计算所有参数,并在计算某些args时完成函数本身的评估。
如何实施。您需要完全重新实施评估系统,我相信没有副作用和懒惰评估的纯函数式编程只会阻碍您。这是一种方法。创建函数,比如paplly :: [ArgumentType] -> TriggerFunction -> ResultType
,其中papply
代表“并行应用”,ArgumentType
是一种计算的实际参数(在你的情况下,它可能是函数关闭+问题到求解),TriggerFunction
是一个函数,在计算其中一个args时调用,在你的情况下ResultType
是布尔值。此功能必须如下工作:
这只是其中一种方法,而不是功能最强大(它使用可变的“内存”)。您还可以与其他参数并行运行触发器功能,并使用某种同步在所有参数之间传递控制。或者您可以使用Erlang或Scala中的某种消息。不幸的是,我没有足够的经验与Haskell编写实际代码,但@Dietrich Epp的帖子似乎代表了类似的想法,所以你可以用它作为基础。
答案 1 :(得分:3)
你想要一个像这样的函数:
majority :: [Bool] -> Bool
你希望它能并行工作。没有汗!不幸的是,我不知道如何在不绕过类型系统的情况下做到这一点。以下是一个示例实现:
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
注意:我不肯定这是正确的。
答案 2 :(得分:2)
你可以在没有平行评估的情况下使用惰性自然来做到这一点。在这种情况下,我选择在hackage上使用peano-inf包:http://hackage.haskell.org/package/peano-inf
import Number.Peano.Inf
import Debug.Trace
import Data.List
myList = [trace "1" True, trace "2" True, trace "3" False, trace "4" True, trace "5" True]
btoNat True = 1 :: Nat
btoNat False = 0 :: Nat
ans = sum $ map btoNat myList
{-
*Main> ans > 2
1
2
3
4
True
-}
请注意,跟踪中不会打印5,因为在此之前评估会被缩短。
要实现并行性,需要手动生成并杀死线程等,这很好,但肯定不太愉快。
请注意,上面的代码使用标准总和。这种不常见的用例就是为什么尽管很多人觉得它不值得,但总和并不尽可能严格。
答案 3 :(得分:1)
我尝试将sclv的解决方案与luqui关于unamb
的评论结合起来,并希望分享我的结果。我将从测试用例开始:
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
示例开始。传递它的投票功能可能如下所示:
voteByTrue list = sum (map bToNat list) >= threshold
where
threshold = (genericLength list + 1) `quot` 2
这与sclv的答案相同。现在我们需要使sum
更加懒惰,以便在遇到undefined
加数时计算不会中止。我对此的第一个看法是:
Zero |+ y = y
Succ x |+ y = Succ (x + y)
instance Num Nat where
x + y = (x |+ y) `lub` (y |+ x)
这里,|+
是第一个参数中的加法运算符strict,而+
在它的两个参数中都是非严格的。它适用于玩具示例,如list1
,但由于线程数呈指数性膨胀,此解决方案的性能会迅速恶化(请参阅每个+
如何生成2个线程,每个线程都是如此再次调用+
,通常使用相同的参数)。有了这样的性能,vote list3
不会足够快地终止。为了解决这个问题,我试图违反unamb
的合同并实施了以下功能:
-- | The same as unamb, but does not have the
-- 'agree unless bottom' precondition.
brokenUnamb = unamb
infoMinMax a b = (x, y)
where
~(x, y) = (a `seq` (b, a)) `brokenUnamb` (b `seq` (a, b))
此函数按其持有的信息量对其两个参数进行排序。它始终返回评估值较少的x
和更多评估值y
。这违反了unamb
论证的条件,从而打破了纯洁。但是,它允许我们更有效地实施+
:
instance Num Nat where
x + y = x' |+ y' where (y', x') = infoMinMax x y
这允许我们通过大型测试(list3
)!现在,进行虚假测试......结果证明infoMinMax
函数在这里也很有用!
vote list = voteByTrue list `maxInfo` voteByFalse list
where
voteByFalse = not . voteByTrue . map not
maxInfo x y = snd (infoMinMax x y)
现在,这允许程序通过所有四个测试,尽管大的测试需要几秒钟才能完成。如果我将undefined
替换为odd (sum [1..])
,CPU使用率也会猛增至200%,因此确实会发生一些并行性。
但是,纯度仍然存在问题。有人可以建议一个简单unamb
足够的解决方案吗?
答案 4 :(得分:-1)
如果我们认为这些函数产生布尔值,问题就变成是否有可能编写一个接受10个布尔值的函数,如果其中6个为真,则返回true,总是需要少于10个输入的值。
一种简单的方法,但是不符合规定要求的方法是测试每个输入依次计算真实数量和真假数量如果trues&gt; = 6 stop return true否则如果falses&gt; = 6 stop return false如果我们在没有触发这些条件的情况下到达最后一个输入,则返回false。因为这会在某些情况下测试所有输入,所以我认为这个问题的答案是否定的,懒惰的评估在这个例子中没有帮助。