且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

递归Haskell函数似乎没有终止

更新时间:2023-02-27 08:56:26

您正在尝试将所有内容放到一个函数中.如果您以模块化的方式工作,将问题分解成更小的问题,那就更好了.

You're trying to put everything together in a single function. It's much better if you work in a modular fashion, breaking the problem into smaller ones.

这是个主意,

  • 生成频率序列,
    f0, f1, f2...
  • 生成频率累积集的序列
    {}, {f0}, {f0,f1}, {f0,f1,f2}...
  • 检查重复插入的内容,即
    fi这样的fi ∈ {f0..fi-1}
  • generate the sequence of frequencies,
    f0, f1, f2...
  • generate the sequence of cumulative sets of frequencies
    {}, {f0}, {f0,f1}, {f0,f1,f2}...
  • check repeated insertions, i.e.
    fi such that fi ∈ {f0..fi-1}

为了使最后一点更清楚,请考虑

To make things clearer regarding the last point consider,

 f0, f1,   f2,      f3...
 {}, {f0}, {f0,f1}, {f0,f1,f2}...`

如果f3是重复,则f3 ∈ {f0,f1,f2}

这似乎效率很低,但是由于Haskell懒惰,因此将根据需要生成这些列表.

This may seem terribly inefficient but because Haskell is lazy, these lists will be generated as needed.

我们需要导入模块以使用集合和也许,

We'll need to import modules to work with sets and maybes,

import Data.Set
import Data.Maybe

可以通过scanl (+)从第一个频率生成频率并列出频率变化列表.函数scanl (+) x xsx开始,使用运算符+操作xs的元素,从而生成总和列表.

Generating the frequencies from the first frequency and a list of frequency changes can be done via scanl (+). The function scanl (+) x xs operates the elements of xs with the operator + , starting at x, generating the cumulative list of sums.

freqs :: Int -> [Int] -> [Int]
freqs = scanl (+)  

现在我们可以生成集合列表.在这里,我们也使用scanl.在每个步骤中,我们都会插入一个新的频率,然后从空集开始.

Now we can generate the list of sets. Here too we use a scanl. In each step we insert a new frequency, and we start with the empty set.

sets  :: [Int] -> [Set Int]
sets  = scanl (\s x -> insert x s) (empty) 

一旦我们有了频率和集合,我们就完成了大部分工作.main函数将所有内容放在一起.它组合两个列表,并找到第一对(fi , {f0,...,fi-1}),例如fi ∈ {f0,...,fi-1},并返回相应的fi

Once we have the frequencies and the sets we are pretty much done.The main function just puts everything together. It combines both lists and finds the first pair (fi , {f0,...,fi-1}) such that fi ∈ {f0,...,fi-1}, and returns the corresponding fi

result :: Int -> [Int] -> Maybe Int
result x xs = let fs = freqs x xs
                  ss = sets fs                  
                  r  = find (\(y,s) -> y `elem` s) (zip fs ss)
              in fmap fst r

注意find返回Maybe (Int, Set Int).它可能会找到Nothing或为s中已经存在的某个频率x返回Just (x,s).我们使用fmap fstJust (x,s)转换为Just x.

Note find returns a Maybe (Int, Set Int). It may find Nothing or return Just (x,s) for some frequency x that was already in s. We use fmap fst to turn Just (x,s) into Just x.

编辑

一旦您愿意的话,可以优化一些东西,或者尝试自己的风格.以下是更简洁的版本,可能是更有效的版本.

Once you've got things working if you wish to, may optimize a few things, or play around with your style. The following is a more succinct, and possibly a little bit more efficient version.

可以一次性建立频率和频率列表.

The list of frequencies and sets can be built together in one go.

freqsets :: Int -> [Int] -> [(Int, Set Int)]
freqsets f0 = scanl (\(f,s) x -> (f+x,insert f s)) (f0,empty) 

因此可以随时将其用于result函数.同样,我们可以利用Maybe作为monad的优势使事情更具可读性.

And so it's ready to use for the result function. Also we can take advantage of Maybe being a monad to make things a bit more readable.

result :: Int -> [Int] -> Maybe Int
result f0 xs = do (f,_) <- find(\(y,s)->y `elem` s) (freqsets f0 xs)
                  return f 

就在那里,这是一个相当短的解决方案.我喜欢结果函数中的更改.我喜欢do表示法,也没有让它计算前两个列表的压缩率.我不确定将两个列表的构建融合"是否值得.可读性较差.***使用三种功能,一种用于频率,一种用于组,另一种用于拉链.

And there you have it, a rather short solution. I like the change in the result function. I like the do notation, as well as not having it calculate the zipping of the two previous lists. I'm not so sure if "fusing" the building of both lists is worth it. It's a bit less readable. Using three functions, one for frequencies, one for sets, and one for zipping, might be best.