关于haskell中的两个变量的迭代

时间:2009-08-07 15:02:18

标签: loops haskell recursion

好的,继续解决Project Euler上的问题,我仍然开始学习Haskell和编程。

我需要找到可被数字整除的最小数字1:20

所以我开始:

divides :: Int -> Int -> Bool
divides d n = rem n d == 0

divise n a  | divides n a == 0 = n : divise n (a+1) 
        | otherwise        = n : divise (n+1) a

我想要发生的是让它继续向上移动n值,直到一个神奇地被[1..20]整除。 但这不起作用,现在我被困在这里从哪里开始。我假设我需要使用:     [1..20] 对于a的值,但我不知道如何实现它。

4 个答案:

答案 0 :(得分:1)

好吧,最近我自己解决了欧拉问题,我很想发布我的答案,但现在我会弃权。 :)


现在,你的程序流程有点混乱,听起来像风水人。基本上,你试图做一件事:增加n直到1..20除以n。但实际上,您应该将其视为两个步骤。

目前,您的代码说:“如果a不除n,则递增n。如果a除以n,则递增a”。但那并不是你想说的。

你想(我想)说“增加n,看看它是否分开[编辑:所有数字1..20]。如果没有,再次增加n,再次测试,等等。”那么你想要做的是进行一个子测试:一个接受一个数字,然后根据1..20进行测试,然后返回一个结果。

希望这有帮助!享受欧拉问题!

编辑:我真的,真的应该记住所有的话。

答案 1 :(得分:1)

嗯,作为一种算法,这种方式很糟糕。

对不起。

但你被列表误导了。我认为你要做的是迭代所有可用的数字,直到找到一个[1..20]中的所有数字。在上面的实现中,如果a不除n,你永远不会回去检查b< a为n + 1。

您的算法的任何简单实现都将是:

lcmAll :: [Int] -> Maybe Int
lcmAll nums = find (\n -> all (divides n) nums) [1..]

(使用Data.List.findData.List.all)。

更好的算法是使用foldl:

成对找到lcm
lcmAll :: [Int] -> Int
lcmAll = foldl lcmPair 1

lcmPair :: Int -> Int -> Int
lcmPair a b = lcmPair' a b
   where lcmPair' a' b' | a' < b' = lcmPair' (a+a') b'
                        | a' > b' = lcmPair' a' (b + b')
                        | otherwise = a'

当然,您可以使用Prelude中的lcm函数而不是lcmPair。

这是有效的,因为任何一组数字的最小公倍数与[这些数字中两个的最小公倍数]和[其余数字]的最小公倍数相同

答案 2 :(得分:1)

函数'divise'永不停止,它没有基本情况。两个分支都调用divise,因此它们都是递归的。您还使用函数divides,就像它将返回一个int(如rem那样),但它返回Bool

我发现你已经开始将问题分成几部分,这通常有助于理解并使其更容易阅读。

另一件可以帮助的事情是编写函数的类型。如果您的功能有效,但您不确定其类型,请尝试使用ghci中的:i myFunction。在这里,我修复了divides中的类型错误(尽管还有其他错误):

*Main> :i divise
divise :: Int -> Int -> [Int]   -- Defined at divise.hs:4:0-5

您希望它返回列表吗?

让您解决问题,尝试进一步将问题分成几部分。这是一种天真的方式:

  1. 检查一个数字是否可被另一个数字整除的函数。这是您的divides功能。

  2. 检查数字是否可以被所有数字[1..20]分割的功能。

  3. 尝试迭代所有数字并在#2中的函数上尝试它们的函数。

答案 3 :(得分:0)

这是我的快速,更多Haskell-y方法,使用您的算法

Prelude> let divisibleByUpTo i n = all (\x -> (i `rem` x) == 0) [1..n]
Prelude> take 1 $ filter (\x -> snd x == True) $ map  (\x -> (x, divisibleByUpTo x 4)) [1..]
[(12,True)]

divisibleByUpTo返回一个布尔值,如果数字i可以被n的每个整数整除,类似于divides函数。

对于Haskell的新人来说,下一行可能看起来很难,所以我会逐一解释:

从右侧开始,我们map (\x -> (x, divisibleByUpTo x 4)) [1..]对每个号码x说明从1开始,执行divisibleByUpTo x 4并将其返回到(x, divisibleByUpTo x 4)的元组中。我正在使用一个元组,所以我们知道哪个数字完全分开。

除此之外,我们有filter (\x -> snd x == True);意味着仅返回元组的第二项为True的元素。

在声明的最左边,我们take 1因为否则我们会有无限的结果列表。

这需要相当长的时间才能达到20的值。就像其他人说的那样,你需要一个更好的算法 - 考虑如何得到4的值,即使我们的“输入”数字是1-2-3-4 ,最终答案只是3 * 4的产物。想想为什么1和2从等式中“掉线”。