首页 > 解决方案 > 在 Haskell 中满足某个要求时突破 If-Then

问题描述

我被分配在 Haskell 中编码一个冰雹序列。必须给我一个整数并创建一个以最后一个数字 1 结尾的整数列表,例如。

-- > hailstone 4
-- [4,2,1]
-- > hailstone 6
-- [6,3,10,5,16,8,4,2,1]
-- > hailstone 7
-- [7,22,11,34,17,52,26,13,40,20,10,5,16,8,4,2,1]

我的答案最后应该只有一个 1,但是一旦达到 1,我不知道如何跳出循环。

hailstone :: Integer -> [Integer]
hailstone = takeWhile (>=1) . (iterate collatz)
  where collatz n = if n == 1
                    then 1
                    else if even n 
                    then n `div` 2 
                    else 3*n+1

最后我得到了无限的 1。我怎样才能解决这个问题?

标签: haskell

解决方案


您可以使用包 [hackage]takeUntil :: (a -> Bool) -> [a] -> [a]中的函数。该功能将:utility-ht

取所有元素,直到一个匹配。匹配的元素也被返回。这是与takeWhile (not . p). 它成立takeUntil p xs == fst (breakAfter p xs)

所以我们可以用它来包括1

import Data.List.HT(takeUntil)

hailstone :: Integer -> [Integer]
hailstone = takeUntil (== 1) . iterate collatz
  where collatz 1 = 1
        collatz n | even n = div n 2
                  | otherwise = 3 * n + 1

或者我们可以实现takeUntil自己:

takeUntil :: (a -> Bool) -> [a] -> [a]
takeUntil p = go
    where go [] = []
          go (x:xs) | p x = [x]
                    | otherwise = x : go xs

或折叠:

takeUntil :: (a -> Bool) -> [a] -> [a]
takeUntil p = foldr (\x y -> x : if p x then [] else y) []

对于负数,collatz可能会陷入无限循环:

Prelude> hailstone (-7)
[-7,-20,-10,-5,-14,-7,-20,-10,-5,-14,-7,-20,-10,-5,-14,-7,-20,-10,-5,-14,

因此,我们可能希望更改所有小于或等于 的数字的条件1

hailstone :: Integer -> [Integer]
hailstone = takeUntil (<= 1) . iterate collatz
  where collatz 1 = 1
        collatz n | even n = div n 2
                  | otherwise = 3 * n + 1

推荐阅读