Haskell中fromJust的正当性

Haskell中fromJust的正当性,haskell,monads,maybe,Haskell,Monads,Maybe,我试图学习Haskll,所以我在Haskell中尝试了Euler项目的第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 &l

我试图学习Haskll,所以我在Haskell中尝试了Euler项目的第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
answer26=answer26'1000
answer26'n=snd$最大值$映射(\x->循环长度x[1])[2..n-1]
哪里
循环长度n(r:rs)
|i/=Nothing=(1+fromJust i,n)
|r

我意识到这不是最有效的算法,但考虑到它是天真的O(n^3)(其中n=1000),这不是一个问题。但我担心的是,从我对单子的理解来看,它们的一个主要特性是,它们在某种意义上“标记”了任何使用过单子的东西。函数“fromJust”似乎直接与此相反。它为什么存在?另外,假设它的存在是合理的,那么我在上述代码中使用它是否是一种良好做法?

通常不鼓励使用部分函数(可能不返回值的函数)。像
head
fromJust
这样的函数之所以存在,是因为它们偶尔很方便;您有时可以编写更短的代码,这对学习者来说更容易理解。许多函数算法都是用
表示的,而
fromJust
在概念上与
相同

通常最好使用模式匹配,并避免使用部分函数,因为它允许编译器为您捕获错误。在您的代码片段中,您仔细检查了值是否从不为
Nothing
,但在大型的实际代码库中,代码可能有很多年的历史,1000行长,并且由许多开发人员维护。开发人员很容易重新订购一些代码而错过这样的检查。对于模式匹配,它就在代码结构中,而不仅仅是在一些任意布尔表达式中

用模式匹配取代您对
的使用并不太困难:

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
answer26=answer26'1000
answer26'n=snd$最大值$映射(\x->循环长度x[1])[2..n-1]
哪里
循环长度n(r:rs)=案例elemIndex r rs
仅i->(1+i,n)
无->如果r
而且(我认为)结果也更清楚一些


编辑:中提到的
中有一个明显的“理论上可以使用”的地方,尽管您需要我以外的人来解释wtf的全部内容……)

monad接口不包括任何用于从monad“提取”值的特定函数,只用于将值放入(
返回

然而,它也不禁止这些类型的函数。当它们存在时,它们将特定于每个monad(因此有许多run*函数:
runIdentity
runReader
runWriter
runState
…每个函数都有不同的参数。)

根据设计,
IO
没有任何这样的“get out”函数,因此它可以在monad中“trap”不纯值。但是“不能出去”一般来说并不是单子的要求。重要的是他们尊重单子定律

随着时间的推移,情况发生了逆转。有一个通用函数从中提取值(
extract
),每个comonad都必须实现。但是“放入值”的函数(当它们存在时)对于每个特定的comonad(,…)是不同的


至于
fromJust
,最好尽可能避免使用它,因为它是一个部分函数,在运行时可能无法匹配。

这种模式非常常见,甚至有一个函数用于此:maybe::b->(a->b)->maybe a->b

在您的情况下,如果执行\x->(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
cyclelelength n(r:rs)=可能(cyclelelength n rs')(1+$elemIndex r rs其中
rs'
|r

另外,因为您只查找最大值,而不是实际值,所以即使使用id而不是(1+)。

在“cycleLength”的第一行中,“i”是什么?哎呀,我忘了在最后一行添加。我现在已经添加了它,但是谢谢。不过,您可以使用just
中的
。我刚才在一个玩具示例中使用了它,因为我得到了算术基本定理的支持。当你真的,真的知道在这种情况下,零值是绝对不可能出现的,这没关系。@kqr通常,在这种情况下,最好还是避免从just
中选择
,并用类似
错误的东西显式处理
零值
,算术的基本定理说这是不可能发生的“
。这样,任何读者都清楚为什么忽略
Nothing
是安全的,如果代码重构时引入了允许出现
Nothing
的bug,那么产生的错误会给您带来更多帮助。不过,这取决于情况,是否真的值得这么麻烦;我们都使用了来自Just的
。你能解释一下为什么模式匹配更安全吗?@Daishisan它更安全,因为如果你错过了一个案例,编译器会警告你。默认情况下,它实际上没有启用,但您可以通过使用
-fwarn complete patterns
-Wall
添加来启用一长串警告()来启用它,正如我已经提到的,在代码结构中显式显示可能的分支的好处是,更清晰,更不容易在以后更改代码时意外丢失。