Haskell 检查函数是否两次返回相同的值

时间:2021-07-24 20:25:04

标签: list haskell divider

我正在做一个项目,遇到了一个有趣的问题。我有一个函数,我们称之为 foo 这个函数将给定的数字除以最大可能的除法器,而不是除以本身。如果最高分频器为 1,则它返回给定的数字。例如,数字 21 首先除以 7 并返回 3 + 它一直返回 3,因为除了 3 和 1 之外没有其他除法器。

ghci> foo 21
3
foo it
3

现在我想将这些步骤保存到返回数组中所有步骤的函数中。目前的实现是:

bar:: int-> [int]
bar x = x : bar (foo x)

这个方法的问题在于它不断返回相同的值并且永无止境。有没有办法检查 foo 的前一次迭代是否返回相同的数字并停止,以免导致无限列表?

类似于:

bar 21
[3]
instead of:
bar 21
[3,3,3,3,3,3,3,3,3...]

在 oop 中,我会将最后一个值保存到 var 中,然后进行比较,但是在 haskell 中情况并非如此,我不确定还能如何实现

2 个答案:

答案 0 :(得分:4)

您应该检查 x 是否等于 foo x,如果是这样,我们可以停止递归,因此:

bar :: Int -> [Int]
bar x
    | x == x' = [x]
    | otherwise = x : bar x'
    where x' = foo x

您在这里也首先返回给定的值。如果列表中的第一项应该是数字除以最大的分隔符,您可以使用:

bar :: Int -> [Int]
bar = go . foo
  where go x
          | x == x' = [x]
          | otherwise = x : go x'
          where x' = foo x

答案 1 :(得分:1)

如果首选库样式,还可以使用 unfoldr 库函数。

通用代码如下:

 λ> 
 λ> :type unfoldr
 unfoldr :: (b -> Maybe (a, b)) -> b -> [a]
 λ> 
 λ> stepFn f v = if (v == f v) then Nothing else Just (let fv = f v in (fv, fv))
 λ> bar f v0 = v0 : L.unfoldr (stepFn f) v0
 λ> 

作为一只小白鼠,我们可以选择一个函数来迭代地寻找整数平方根,比如 709。请注意,如果没有一些额外的调整,它并不适用于所有数字,这只会模糊我们在这里的目的。

>
 λ> 
 λ> foo n = div (n + div 709 n) 2
 λ> 
 λ> foo 1
 355
 λ> 
 λ> foo 355
 178
 λ> 
 λ> foo 26
 26
 λ> 

那么让我们试试 bar

 λ> 
 λ> :type bar
 bar :: Eq b => (b -> b) -> b -> [b]
 λ> 
 λ> bar foo 1
 [1,355,178,90,48,31,26]
 λ>