【问题标题】:Do while loop in Haskell在 Haskell 中执行 while 循环
【发布时间】:2011-05-12 11:14:01
【问题描述】:

我有一个函数:

isItSimple :: Int -> Bool

它获取 Int 并返回 Bool。

我需要在 [x | 中找到第一个数x n..], isItSimple x].

这是我的解决方案:

findIt :: Int -> Int
findIt num
       | isItSimple num = num
       | otherwise = findIt (num + 1)

Haskell 有没有更好的解决方案?

【问题讨论】:

    标签: haskell recursion


    【解决方案1】:

    我需要在 [x | 中找到第一个数x

    就如你所说。

    findIt n = head [ x | x <- [n..], isItSimple x ]
    

    【讨论】:

    • 绝对优于fromJust
    • @FUZxxl 我一般不喜欢fromJust 但在这种情况下,它与head 完全相同,我的意思是在n 之上没有x 满足isItSimple 将产生“底部”。无论如何,我们永远不会评估 head []fromJust Nothing
    【解决方案2】:

    虽然其他答案有效,但可以说它们不是在 Haskell 中解决此问题的最惯用的方法。您真的不需要任何额外的导入:Prelude 中的几个函数就可以解决问题。

    我首先创建一个包含所有大于或等于n 的简单数字的列表。函数filter :: (a -&gt; Bool) -&gt; [a] -&gt; [a] 让这一切变得简单:

    filter isItSimple [n..]
    

    就像[n..] 一样,这是一个无限列表,但这不是问题,因为 Haskell 是惰性的,在需要之前不会评估任何内容。

    要得到你想要的,你可以占据这个无限列表的头部:

    findIt :: Int -> Int
    findIt n = head $ filter isItSimple [n..]
    

    有些人不喜欢head,因为它是一个偏函数,当它给出一个空列表时会引发异常。我个人不会在这里担心这一点,因为我们知道它永远不会在空列表中被调用。它比fromJust 让我不那么不舒服,这也是一个部分函数(在给定Nothing 时会引发异常),在我看来总是一个坏主意。

    (说到个人品味,我会这样写:

    findIt = head . filter isItSimple . enumFrom
    

    这是pointfree 样式的示例,它可能会变得令人费解,但在我看来,在这种情况下非常优雅。)

    【讨论】:

    • 担心headfromJust 在这种情况下无论如何都是多余的,因为如果不存在简单数字,程序将首先进入无限循环。但我同意fromJust 几乎从来都不是一个好主意。至少使用fromMaybe (error "What? Inconceivable!") 来明确发生了什么。
    • @camccann:是的,但对我来说,更担心的是养成更好的编码习惯。如果我强迫自己假装fromJust 不存在,我想我会写出更好的代码。
    【解决方案3】:

    在大多数情况下,尤其是当您的问题是已解决的特定情况时,显式递归是不好的。不使用显式递归的问题的一种可能解决方案是:

    import Data.List (find)
    import Data.Maybe (fromJust)
    
    findIt :: Int -> Int
    findIt n = fromJust $ find isItSimple [n..]
    

    【讨论】:

    • 这不取决于问题的复杂性和解决方案吗?如果显式递归比了解 find 和 fromJust 更简单,难道不应该更喜欢使用显式递归吗?
    • @Jaywalker:一模一样,而且更优雅。
    • 谁能详细说明“显式递归不好”的含义。表现不佳?丑吗?
    • @MartinCapodici 主要是丑陋。已经有人解决了你的问题,为什么还要再解决呢?它通常也更容易阅读,因为函数名称描述了你在做什么。
    【解决方案4】:
    findIt :: Int -> Int
    findIt num = head $ dropWhile (not isItSimple) [num..]
    

    我不知道它是否更好。我刚想到它。

    【讨论】:

    • 应该是(not . isItSimple),带点(功能组合)。
    【解决方案5】:

    另一种方法是使用最小不动点组合器(在 Data.Function 中修复)

    findIt = fix (\f x ->  if isItSimple x then x else f (x + 1))
    

    在这种情况下,它看起来有点过度设计,但如果“搜索空间”遵循比x + 1 更复杂的规则,这种技术可能会非常有用。

    【讨论】:

    • 不要把 fix 误认为是递归以外的任何东西。通过机械替换:findIt x = if isItSimple x then x else findIt (x + 1).
    • IOW,head . dropWhile (not . isItSimple) . iterate (1+),甚至只是until isItSimple (1+)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-06-08
    • 1970-01-01
    • 2020-09-19
    • 2021-06-30
    • 2015-04-24
    • 2012-08-24
    • 1970-01-01
    相关资源
    最近更新 更多