无法匹配 foldl 内的元组

Can't match tuple inside of foldl

我有以下代码,它应该™ 将 excel 列类型转换为相应的数字。例如AA变成27,AB变成28:

import Data.Char (ord)
import Data.List (foldl1')

columnToNumber :: String -> Int
columnToNumber s = foldl1' (\acc (i, v) -> acc + 26 ^ i * v) (values s)
  where values s = zip (reverse [0..(length s)]) ((\c -> ord c - 64) <$> s)

思路是把字符串"AA"转换成对应的数字

["A", "A"] -> [1, 1]

然后将其与底座一起压缩,从右到左 26^0、26^1、26^2 等等。

zip [1, 0] [1, 1] -> [(1, 1), (0, 1)]

这样折叠的结果就是

26^1 * 1 + 26^0 * 1 = 27

不幸的是,我收到以下错误,我不确定原因:

ExcelSheetColumn.hs:7:34:
    Couldn't match expected type ‘Int’
                with actual type ‘(Integer, Int)’
    In the pattern: (i, v)
    In the first argument of ‘foldl1'’, namely
      ‘(\ acc (i, v) -> acc + 26 ^ i * v)’
    In the expression:
      foldl1' (\ acc (i, v) -> acc + 26 ^ i * v) (values s)

ExcelSheetColumn.hs:7:63:
    Couldn't match type ‘(Int, Int)’ with ‘Int’
    Expected type: [Int]
      Actual type: [(Int, Int)]
    In the second argument of ‘foldl1'’, namely ‘(values s)’
    In the expression:
      foldl1' (\ acc (i, v) -> acc + 26 ^ i * v) (values s)

有人可以帮我吗?

要编译它,您实际上只需将 foldl1' 切换为 foldl' 并添加 starting 累加器:

import Data.Char (ord)
import Data.List (foldl')

columnToNumber :: String -> Int
columnToNumber s = foldl' (\acc (i, v) -> acc + 26 ^ i * v) 0 (values s)
  where values s = zip (reverse [0..(length s)]) ((\c -> ord c - 64) <$> s)

如果您添加 Free_D 提出的建议(从 length s - 1 开始):

columnToNumber :: String -> Int
columnToNumber s = foldl' (\acc (i, v) -> acc + 26 ^ i * v) 0 (values s)
  where values s = zip (reverse [0..(length s -1)]) ((\c -> ord c - 64) <$> s)

您得到了想要的结果:

λ> columnToNumber "AA"
27
λ> columnToNumber "AB"
28

我不知道你是否真的需要这个,但嘿,为什么不呢:

您可能不喜欢的是 columnToNumber "A1"11 - 要解决此问题,您需要使用不同于字母的数字:

columnToNumber :: String -> Int
columnToNumber s = foldl' (\acc (i, v) -> acc + 26 ^ i * v) 0 (values s)
  where values s = zip (reverse [0..(length s -1)]) (parse <$> s)
        parse c
          | c >= '0' && c <= '9' = ord c - ord '0'
          | otherwise = ord c - 64

查看foldl1'的定义,它必须取两个相同类型的东西并产生相似的东西

*Main Data.List> :t foldl1'
foldl1' :: (a -> a -> a) -> [a] -> a

但是foldl是你想要的:

*Main Data.List> :t foldl
foldl :: Foldable t => (b -> a -> b) -> b -> t a -> b

基本上是这样的:

import Data.Char (ord, toUpper)

columnToNumber :: String -> Int
columnToNumber s = foldl (\acc (i, v) -> acc + 26 ^ i * v) 0 $ values s where 
    values s = zip [l - 1, l - 2 ..0] ((\c -> (ord.toUpper) c - 64) <$> s) where
        l = length s