您试图将所有内容放在一个函数中。如果您以模块化方式工作,将问题分解为更小的问题,那就更好了。
这是一个想法,
- 生成频率序列,
f0, f1, f2...
- 生成累积频率集的序列
{}, {f0}, {f0,f1}, {f0,f1,f2}...
- 检查重复插入,即
fi
这样fi ∈ {f0..fi-1}
为了使最后一点考虑的事情更清楚,
f0, f1, f2, f3...
{}, {f0}, {f0,f1}, {f0,f1,f2}...`
if f3
那时是重复f3 ∈ {f0,f1,f2}
这看起来效率极低,但由于 Haskell 很懒,这些列表将根据需要生成。
我们需要导入模块来处理集合,也许,
import Data.Set
import Data.Maybe
从第一个频率生成频率和频率变化列表可以通过scanl (+)
。功能scanl (+) x xs
操作的元素xs
与操作员+
, 开始于x
,生成总和的累积列表。
freqs :: Int -> [Int] -> [Int]
freqs = scanl (+)
现在我们可以生成集合列表。这里我们也使用一个scanl
。在每个步骤中,我们插入一个新频率,并从空集开始。
sets :: [Int] -> [Set Int]
sets = scanl (\s x -> insert x s) (empty)
一旦我们有了频率和集合,我们就差不多完成了。 main 函数只是将所有内容放在一起。它组合两个列表并找到第一对(fi , {f0,...,fi-1})
这样fi ∈ {f0,...,fi-1}
,并返回对应的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
Note find
返回一个Maybe (Int, Set Int)
。它可能会发现Nothing
或返回Just (x,s)
对于某些频率x
那已经在s
。我们用fmap fst
转动Just (x,s)
into Just x
.
EDIT
一旦你的工作顺利进行,如果你愿意的话,可以优化一些东西,或者尝试一下你的风格。以下是一个更简洁、可能更有效的版本。
频率和集合的列表可以一次性构建在一起。
freqsets :: Int -> [Int] -> [(Int, Set Int)]
freqsets f0 = scanl (\(f,s) x -> (f+x,insert f s)) (f0,empty)
因此它可以用于结果函数。我们也可以利用Maybe
成为一个 monad 让事情变得更具可读性。
result :: Int -> [Int] -> Maybe Int
result f0 xs = do (f,_) <- find(\(y,s)->y `elem` s) (freqsets f0 xs)
return f
这就是一个相当简短的解决方案。我喜欢结果函数的变化。我喜欢do
符号,以及不让它计算前两个列表的压缩。我不太确定“融合”两个列表的构建是否值得。可读性有点差。使用三种函数,一种用于频率,一种用于集合,一种用于压缩,可能是最好的。