Не удается сопоставить кортеж внутри 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)

Может ли кто-нибудь помочь мне?


person m0meni    schedule 11.05.2016    source источник
comment
Ну, во-первых, я думаю, вы хотите использовать length s - 1 в своей функции значений.   -  person Free_D    schedule 11.05.2016


Ответы (2)


чтобы его скомпилировать, вам просто нужно переключить foldl1' на foldl' и добавить стартовый аккумулятор:

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
person Random Dev    schedule 11.05.2016
comment
Ах, foldl1 -> foldl было так просто. - person m0meni; 11.05.2016

Глядя на определение 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
person smac89    schedule 11.05.2016
comment
[l - 1, l - 2 ..0] понятия не имел, что ты можешь это сделать! - person m0meni; 11.05.2016