我正在努力学习Haskll,所以我在Haskell中尝试了Project Euler的问题26: http://projecteuler.net/problem=26
我对此问题的解决方案是:
answer26 = answer26' 1000
answer26' n = snd $ maximum $ map (\x -> cycleLength x [1]) [2..n - 1]
where
cycleLength n (r:rs)
| i /= Nothing = (1 + fromJust i, n)
| r < n = cycleLength n $ (10*r):r:rs
| otherwise = cycleLength n $ (r `mod` n):r:rs
where i = elemIndex r rs
我意识到这不是最有效的算法,但看到它是天真的O(n ^ 3)(其中n = 1000)不是这样的问题。我关心的是,根据我对monad的理解,他们的主要特性之一是它们在某种意义上“标记”任何使用过monad的东西。功能“fromJust”似乎直接面对它。它为什么存在?另外,假设它的存在是合理的,我在上面的代码中使用它是否很好?
答案 0 :(得分:11)
通常不鼓励使用部分函数(可能不返回值的函数)。存在head
和fromJust
等函数,因为它们偶尔会很方便;你有时可以编写更短的代码,这对学习者来说更容易理解。许多功能算法以head
和tail
表示,fromJust
在概念上与head
相同。
通常最好使用模式匹配,并避免使用部分函数,因为它允许编译器为您捕获错误。在您的代码片段中,您仔细检查过该值永远不会Nothing
,但在大型现实代码库中,代码可能需要很多年,1000多行代码并且由许多开发人员维护。开发人员很容易重新订购一些代码并错过这样的支票。通过模式匹配,它就在代码结构中,而不仅仅是在一些任意的Bool表达式中。
用模式匹配替换fromJust
的用法并不太难:
answer26 = answer26' 1000
answer26' n = snd $ maximum $ map (\x -> cycleLength x [1]) [2..n - 1]
where
cycleLength n (r:rs) = case elemIndex r rs of
Just i -> (1 + i, n)
Nothing -> if r < n
then cycleLength n $ (10*r):r:rs
else cycleLength n $ (r `mod` n):r:rs
而且(我认为)结果也更清晰了。
编辑:在Typeclassopedia中使用fromJust
显然有“理论上可行”的地方,但你需要我以外的其他人来解释wtf ..;)
答案 1 :(得分:10)
monad接口不包含任何用于从monad中“提取”值的特定函数,仅用于将它们放入(return
)中。
但是,它也没有禁止这些功能。当它们存在时,它们将特定于每个monad(因此多个run *函数:runIdentity
,runReader
,runWriter
,runState
...每个都有不同的参数。)
按照设计,IO
没有任何这样的“离开”功能,因此它用于“捕获”monad中的不纯值。但是“不能出去”并不是一般的单身人士的要求。重要的是他们尊重monad法律。
comonads,情况正好相反。有一个通用函数可以从中提取每个comonad必须实现的值(extract
)。但是,“存放价值”的功能(如果存在的话)因每个特定的共同点而异(env,store ......)
对于fromJust
,最好尽可能避免使用它,因为它是一个部分函数,可能在运行时无法匹配。
答案 2 :(得分:3)
这种模式非常普遍,甚至还有一个功能:maybe :: b - &gt; (a - &gt; b) - &gt;也许是 - &gt; B'/ P>
在你的情况下,如果你做\ x - &gt; (cycleLength x [1],x),即在cycleLength之外构造对:
cycleLength n (r:rs) = maybe (cycleLength n rs') (1+) $ elemIndex r rs where
rs'
| r < n = (10*r):r:rs
| otherwise = (r `mod` n):r:rs
此外,因为您只是寻找最大值而不是实际值,所以即使使用id而不是(1 +)也可以使用。