我需要生成一个无限的Haskell列表,该列表包含一个整数的倒数的小数部分(以MSB优先顺序)的所有位(或单词)。是否可以从标准库中直接做到这一点,还是需要实现牛顿迭代函数或类似函数?
我考虑过使用CReal,但是找不到提取位/单词的方法。
答案 0 :(得分:2)
有更多原则性的方法,但是CReal当然可以做到。
> bit n = (`mod` 2) . floor . (*2**n)
> [bit i (pi :: CReal) | i <- [-1..10]]
[1,1,0,0,1,0,0,1,0,0,0,0]
> [bit i (5/8 :: CReal) | i <- [1..10]]
[0,1,0,1,0,0,0,0,0,0,0]
随心所欲地交换2
。对于无限列表,迭代乘法可能会更便宜,所以:
> bits = map ((`mod` 2) . floor) . iterate (2*)
> take 10 (bits (5/8 :: CReal))
[0,1,0,1,0,0,0,0,0,0]
同样,您可以将2
换成您喜欢的任何基地。
答案 1 :(得分:2)
在我的另一个答案中,我将展示如何使用CReal来执行此操作,以回答您是否可以做到这一点的问题。但是我实际上并不认为这是一个好主意,因为它调用的功率超过了所需。只是为了给我一种“更原则性”的方法一个想法,这就是我的想法:
bits :: Rational -> [Int]
bits n = whole : bits (2*frac) where
(whole, frac) = properFraction n
实际情况:
> take 65 . bits $ 1/3
[0,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1]
您会注意到,这比其他答案中的CReal方法显着更快。即使您将其他答案从CReal切换到Rational,它也应该具有更好的内存性能,因为它将分数保持上限为1(而其他解决方案每次迭代增加〜1位)。可以通过注意到它开始循环来使其变得更快。这是一个以易于观察的方式返回循环的函数:
import Data.Set (Set)
import qualified Data.Set as S
data BitsRep
= Loop Rational [Int]
| Lollipop [Int] [Int]
deriving (Eq, Ord, Read, Show)
-- always returns a Lollipop when given an empty set
bitsRaw :: Set Rational -> Rational -> BitsRep
bitsRaw s n = case S.member n s of
True -> Loop n []
False -> case bitsRaw (S.insert n s) (2*frac) of
Lollipop prefix loop -> Lollipop (whole:prefix) loop
Loop n' loop -> (if n == n' then Lollipop [] else Loop n') (whole:loop)
where
(whole, frac) = properFraction n
如果您真的希望将其作为一个无限列表,则可以使用一个简短的包装器,并在达到循环点后大大减少所需的计算量:
bits :: Rational -> [Int]
bits n = prefix ++ cycle loop where
Lollipop prefix loop = bitsRaw S.empty n
答案 2 :(得分:2)
经过一番修补,我设法做到了这一点,而没有诉诸于Rational / CReal:
{{1}}
与使用迭代相比,使用显式递归可以获得适度的加速。我不太担心它进入循环的位置,因为我使用的它是如此之大,以至于我永远都不会到达循环。再次感谢您的帮助!