检查列表中的值以查看具有单个变量的Coprime

eLe*_*der 1 recursion haskell

我在Haskell中实现此功能时遇到了一些麻烦.这是我到目前为止:

--Extend coprime to a function on lists:
--coprime_with n list = True exactly when n is coprime with
--every element of list.

coprime_with :: Integer -> [Integer] -> Bool
coprime_with a [] = False
coprime_with a (b:bs) = if ((coprime a b) == True) then 
                          (coprime_with a bs)
                        else False
Run Code Online (Sandbox Code Playgroud)

希望这不是太混乱.请不要只给我答案.只是让我知道我做错了什么以及如何解决它.谢谢!

编辑:好的,所以我想出来了!非常感谢您的回复.它非常快.我是Comp Sci专业,这是我的第一个学期.我的目标是实际学习,而不仅仅是通过它.感谢您的回复以及您的措辞.

我的问题是这样的:

coprime_with a [] = False
Run Code Online (Sandbox Code Playgroud)

什么时候应该这样:

coprime_with a [] = True
Run Code Online (Sandbox Code Playgroud)

现在它正常运作.Phew ...我觉得很愚蠢,我花了五个小时写这个功能,但至少我实际上学到了一些东西.再次感谢!

Wil*_*ess 5

很好,你找到了你的问题.您可以从中获得更多的课程.

所以你现在有

coprime_with :: Integer -> [Integer] -> Bool
coprime_with a []     = True
coprime_with a (b:bs) = if ((coprime a b) == True) then 
                          (coprime_with a bs)
                        else False
Run Code Online (Sandbox Code Playgroud)

每当你看到,如果一个B否则,你可以将其替换为一个 && .你明白为什么吗?

每当你看到A == True时,你可以用A替换它.你明白为什么吗?

所以现在我们有了

coprime_with a (b:bs) = (coprime a b) && (coprime_with a bs)
Run Code Online (Sandbox Code Playgroud)

参数a不会改变,但我们不必要地将它传递给每个递归调用.另一方面,我们必须拥有它,因为它就是coprime_with它的全部.解决方案是引入内部功能,

coprime_with a bs = g bs      -- "g" is "coprime_with a"
   where
     g []     = True
     g (b:bs) = coprime a b && g bs
Run Code Online (Sandbox Code Playgroud)

(我们可以在没有括号的情况下编写它,因为Haskell中的功能应用程序具有最高优先级).

这称为worker/wrapper变换.它使我们的代码更清晰,并允许编译器执行更多优化.我们的新g工作者是工人,并且coprime_with是包裹g在里面的包装工具.g有一个不同的类型,并coprime_with负责为它提供适当的参数(这里只是第二个),并将其结果转换回预期的(这里只是传递它们).

接下来,我们可以在Haskell中为我们想要的任何(子)表达式赋予一个新名称.在这里,我们会这样做

coprime_with a bs = g pred bs    -- "pred" is for "predicate"
   where
     g pred []     = True
     g pred (b:bs) = pred b && g pred bs
     pred = coprime a
Run Code Online (Sandbox Code Playgroud)

有什么意义呢?你问,我们回过头来递送额外的递归参数吗?正确!这不是一个优化,而只是一个重写,旨在到达某个地方.接下来,我们甚至可以替代定义pred

coprime_with a bs = g (coprime a) bs
   where
     g :: (a -> Bool) -> [a] -> Bool
     g pred []     = True
     g pred (b:bs) = pred b && g pred bs
Run Code Online (Sandbox Code Playgroud)

那是我们的目的地.剩下的就是要意识到我们已经重新设计了一个轮子(其中一个轮子).您可以通过在Hoogle上搜索g的类型找到它的类型,并检查那里的几个顶部匹配的来源,看看我们g究竟是哪一个.提示:您已经在评论中建议使用它.


现在我们可以清楚地看到原因,True而不是False必须在空列表中返回.我们g检查谓词是否适用于列表的所有元素.换句话说,它必须失败(这意味着在这里,回报False),如果在列表中的某些元素无法断言-所以,返回True不然!但是空列表中没有元素可以使谓词失败.

(我喜欢这个问题的另一个解释基本上是说,因为g (a++b) == g a && g b而且a == a ++ [],它必须持有g a == g (a++[]) == g a && g []).