4

我一直在尝试使用这个函数并使用 iterate 和 takeWhile 进行小实现。它不一定要使用那些功能,我只是想把它变成一条线。我可以看到其中的模式,但如果不制作相同的代码,我似乎无法利用它,只需使用迭代而不是递归。

fun2 :: Integer -> Integer
fun2 1 = 0
fun2 n 
    | even n = n + fun2 (n `div` 2)
    | otherwise = fun2 (3 * n + 1)

任何帮助都会很棒。我已经为此苦苦挣扎了好几个小时。谢谢

4

3 回答 3

6

如果你想用 来做到这一点iterate,关键是将它分成更小的逻辑部分:

  • 使用规则生成序列

    a k+1 = a k /2 如果 a k偶数

    a k+1 = 3a k +1 如果k奇数

  • 在j = 1处停止序列(如果collat​​z 猜想为真,所有这些都可以)。

  • 过滤掉路径上的偶数元素
  • 总结他们

那么这就变成了:

  f = sum . filter even . takeWhile (>1) . iterate (\n -> if even n then n `div` 2 else 3*n + 1)

但是,我确实认为使用辅助功能会更清楚

  f = sum . filter even . takeWhile (>1) . iterate collatz
    where collatz n | even n    = n `div` 2
                    | otherwise = 3*n + 1

这可能不会为您节省任何行,但会将您的递归转换为数据的生成。

于 2013-02-11T02:04:00.900 回答
3

首先,我同意汤姆的评论,即您的四行版本没有任何问题。它完全可读。然而,偶尔将 Haskell 函数变成一个内衬是一种有趣的练习。谁知道,你可能会学到一些东西!

此刻你有

fun 1 = 0
fun n | even n    = n + fun (n `div` 2)
      | otherwise = fun (3 * n + 1)

您始终可以将使用警卫的表达式转换为if

fun 1 = 0
fun n = if even n then n + fun (n `div` 2) else fun (3 * n + 1)

您始终可以将一系列模式匹配转换为 case 表达式:

fun n = case n of
            1 -> 0
            _ -> if even n then n + fun (n `div` 2) else fun (3 * n + 1)

最后,您可以将 case 表达式转换为ifs 链(实际上,通常这需要一个Eq实例作为函数的参数,但由于您使用的是Integers ,所以没关系)。

fun n = if n == 1 then 0 else if even n then n + fun (n `div` 2) else fun (3 * n + 1)

我想你会同意这比你开始的可读性要差得多。

于 2013-02-11T00:08:16.443 回答
0

一个班轮;)

fun2 n = if n==1 then 0 else if even n then n + fun2 (n `div` 2) else fun2 (3 * n + 1)

我的感觉是缺少查找表,如果没有递归,这个函数就无法实现,因为递归中传递的参数似乎不可预测(n 为 2 的幂除外)。

另一方面,rampion 帮助我学习了一些新东西。

于 2013-02-11T00:09:53.657 回答