Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/list/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
List 我怎样才能在哈斯克尔与州政府合作?_List_Haskell_Fold_Traversable_Foldable - Fatal编程技术网

List 我怎样才能在哈斯克尔与州政府合作?

List 我怎样才能在哈斯克尔与州政府合作?,list,haskell,fold,traversable,foldable,List,Haskell,Fold,Traversable,Foldable,我有一个简单的函数(实际上用于解决Euler项目的一些问题)。它将数字列表转换为十进制数 fromDigits :: [Int] -> Integer fromDigits [x] = toInteger x fromDigits (x:xs) = (toInteger x) * 10 ^ length xs + fromDigits xs 我意识到类型[Int]并不理想fromDigits应该能够接受其他输入,例如序列,甚至可能是可折叠的 我的第一个想法是用一种“状态折叠”来替换上面的

我有一个简单的函数(实际上用于解决Euler项目的一些问题)。它将数字列表转换为十进制数

fromDigits :: [Int] -> Integer
fromDigits [x] = toInteger x
fromDigits (x:xs) = (toInteger x) * 10 ^ length xs + fromDigits xs
我意识到类型
[Int]
并不理想
fromDigits
应该能够接受其他输入,例如序列,甚至可能是
可折叠的


我的第一个想法是用一种“状态折叠”来替换上面的代码。以上功能的正确(=最小)Haskell类别是什么?

首先,折叠已经是关于携带一些状态<代码>可折叠的正是您要寻找的,不需要
状态
或其他单子

其次,在空列表上定义基本情况,然后在非空列表上定义基本情况,这将更为自然。现在的情况是,函数在空列表上没有定义(虽然它完全有效)。请注意,
[x]
只是
x:[]
的简写

在当前形式中,该函数几乎可以使用
foldr
来表达。但是在
foldl
中,列表或其部分不可用,因此无法计算
length xs
。(在每一步计算
length xs
也会使整个函数不必要地变成O(n^2)。)但如果您将过程设置为以相反的方式使用列表,则可以很容易地避免这种情况。函数的新结构可能如下所示:

fromDigits' :: [Int] -> Integer
fromDigits' = f 0
  where
    f s []     = s
    f s (x:xs) = f (s + ...) xs

之后,尝试使用
foldl
来表示
f
,最后将其替换为。

应避免使用
length
,并使用
foldl
编写函数(或
foldl'
):


从这一点来看,对任何可折叠函数的泛化应该是清楚的。

这样一个简单的函数可以在其裸参数中携带其所有状态。携带一个累加器参数,操作就变得微不足道了

fromDigits :: [Int] -> Integer
fromDigits xs = fromDigitsA xs 0  # 0 is the current accumulator value

fromDigitsA [] acc = acc
fromDigitsA (x:xs) acc = fromDigitsA xs (acc * 10 + toInteger x)

解决这个问题的一个更好的方法是列出你的10种力量。使用
迭代
非常简单:

powersOf :: Num a => a -> [a]
powersOf n = iterate (*n) 1
然后你只需要将这些10的幂乘以数字列表中它们各自的值。使用
zipWith(*)
很容易做到这一点,但首先必须确保顺序正确。这基本上只是意味着您应该对数字进行重新排序,使其按降序排列,而不是按升序排列:

zipWith (*) (powersOf 10) $ reverse xs
但是我们希望它返回一个
整数
,而不是
Int
,所以让我们从integral中通过一个
映射

zipWith (*) (powersOf 10) $ map fromIntegral $ reverse xs
剩下的就是对它们进行总结

fromDigits :: [Int] -> Integer
fromDigits xs = sum $ zipWith (*) (powersOf 10) $ map fromIntegral $ reverse xs
或者是免费的球迷

fromDigits = sum . zipWith (*) (powersOf 10) . map fromIntegral . reverse
现在,您还可以使用fold,它基本上只是一个纯for循环,其中函数是您的循环体,初始值是初始状态,您提供的列表是您正在循环的值。在这种情况下,你的状态是一个总和,你有多大的权力。我们可以创建自己的数据类型来表示这一点,或者我们可以使用一个元组,第一个元素是当前总数,第二个元素是当前幂:

fromDigits xs = fst $ foldr go (0, 1) xs
    where
        go digit (s, power) = (s + digit * power, power * 10)
这大致相当于Python代码

def fromDigits(数字):
def go(数字,acc):
s、 功率=acc
返回(s+数字*功率,功率*10)
状态=(0,1)
对于数字中的数字:
状态=开始(数字,状态)
返回状态[0]

如果您真的决定使用正确的折叠方式,您可以将计算
长度xs
与这样的计算结合起来(自由定义
fromDigits[]=0
):

现在很明显,这相当于

fromDigits xn = fst $ foldr (\ x (y, l) -> (toInteger x * 10^l + y, l + 1)) (0, 0) xn
在使用fold编写递归函数时,向累加器中添加额外的组件或结果,并在fold返回时丢弃它的模式是非常普遍的

话虽如此,一个函数在第二个参数中总是严格的
foldr
是一个非常非常糟糕的主意(过度的堆栈使用,可能是长列表上的堆栈溢出),你真的应该像其他一些答案所建议的那样,以
foldl
的形式编写
fromDigits
“fold with state”,可能您正在寻找的抽象是可遍历的

traverse :: Applicative f => (a -> f b) -> t a -> f (t b)
基本上,遍历采用类型为
a->fb
的“有状态函数”,并将其应用于容器
ta
中的每个函数,从而生成一个容器
f(tb)
。在这里,
f
可以是
State
,您可以使用类型为
Int->State Integer()的遍历函数
。它将构建一个无用的数据结构(在您的情况下是单元列表),但您可以放弃它。以下是使用Traversable解决问题的方法:

import Control.Monad.State
import Data.Traversable

sumDigits :: Traversable t => t Int -> Integer
sumDigits cont = snd $ runState (traverse action cont) 0
  where action x = modify ((+ (fromIntegral x)) . (* 10))

test1 = sumDigits [1, 4, 5, 6]
然而,如果您真的不喜欢构建丢弃的数据结构,您可以将
Foldable
与一些棘手的
Monoid
实现结合使用:不仅存储计算结果,还存储
10^n
,其中
n
是转换为该值的位数。此附加信息使您能够进行梳理有两个值:

import Data.Foldable
import Data.Monoid

data Digits = Digits
  { value :: Integer
  , power :: Integer
  }

instance Monoid Digits where
  mempty = Digits 0 1
  (Digits d1 p1) `mappend` (Digits d2 p2) =
    Digits (d1 * p2 + d2) (p1 * p2)

sumDigitsF :: Foldable f => f Int -> Integer
sumDigitsF cont = value $ foldMap (\x -> Digits (fromIntegral x) 10) cont

test2 = sumDigitsF [0, 4, 5, 0, 3]

我坚持第一个实现。虽然它构建了不必要的数据结构,但它更简短、更容易理解(就读者理解的
可遍历的
)。

您的函数不使用状态,但如果使用状态,您可以使用
数据。可折叠的
,它提供
可折叠的::(可折叠的t,Monad m)=>(b->a->mb->b->t a->m b
。顺便说一句,你函数的运行时非常糟糕。非常感谢!我更喜欢user5402的答案,因为它更简洁。@ruben.moor我不想马上给出答案,更多的是如何得到它的提示。
import Control.Monad.State
import Data.Traversable

sumDigits :: Traversable t => t Int -> Integer
sumDigits cont = snd $ runState (traverse action cont) 0
  where action x = modify ((+ (fromIntegral x)) . (* 10))

test1 = sumDigits [1, 4, 5, 6]
import Data.Foldable
import Data.Monoid

data Digits = Digits
  { value :: Integer
  , power :: Integer
  }

instance Monoid Digits where
  mempty = Digits 0 1
  (Digits d1 p1) `mappend` (Digits d2 p2) =
    Digits (d1 * p2 + d2) (p1 * p2)

sumDigitsF :: Foldable f => f Int -> Integer
sumDigitsF cont = value $ foldMap (\x -> Digits (fromIntegral x) 10) cont

test2 = sumDigitsF [0, 4, 5, 0, 3]