编写此函数的更好方法:list to matrix(= list of list)

时间:2017-06-17 18:09:18

标签: list haskell matrix

有没有更好的方法来编写这个函数(即通过折叠在一行中)?

--              list -> rowWidth -> list of lists
listToMatrix :: [a] -> Int -> [[a]]
listToMatrix [] _ = []
listToMatrix xs b = [(take b xs)] ++ (listToMatrix (drop b xs) b)

4 个答案:

答案 0 :(得分:4)

实际上这是一个很好的展开案例。 Data.List方法unfoldr与折叠列表不同,通过向其应用函数直到此函数返回Nothing,从种子值创建列表。在我们到达将返回Nothing的终止条件之前,函数返回Just (a,b),其中a是列表的当前生成项,b是种子的下一个值。在这种特殊情况下,我们的种子值是给定的列表。

import Data.List
chunk :: Int -> [a] -> [[a]]
chunk n = unfoldr (\xs -> if null xs then Nothing else Just (splitAt n xs))

*Main> chunk 3 [1,2,3,4,5,6,7,8,9]
[[1,2,3],[4,5,6],[7,8,9]]
*Main> chunk 3 [1,2,3,4,5,6,7,8,9,10]
[[1,2,3],[4,5,6],[7,8,9],[10]]

答案 1 :(得分:3)

是的,有更好的方法来编写此功能。但我并不认为让它成为一条线会改善任何事情。

使用前置操作符(:)代替列表连接(++)进行单元素前置

表达式[(take b xs)] ++ (listToMatrix (drop b xs) b)效率低下。我并不意味着在性能方面效率低下,因为编译器可能会优化它,但是,在这里,你构建一个列表,然后在其上调用一个函数((++)),它将解构它通过模式匹配。您可以使用(:)数据构造函数直接构建列表,这允许您将单个元素添加到列表中。表达式变为take b xs : listToMatrix (drop b xs) b

使用splitAt以避免两次运行列表

import Data.List (splitAt)

listToMatrix :: [a] -> Int -> [[a]]
listToMatrix xs b = row : listToMatrix remaining b
    where (row, remaining) = splitAt b xs

使用Maybe

确保数据的正确性
listToMatrix :: [a] -> Int -> Maybe [[a]]
listToMatrix xs b
    | length xs `mod` b /= 0 = Nothing
    | null xs   = Just []
    | otherwise = Just $ row : listToMatrix remaining b
    where (row, remaining) = splitAt b xs

通过定义辅助函数,您甚至可以避免每次检查是否有正确数量的元素:

listToMatrix :: [a] -> Int -> Maybe [[a]]
listToMatrix xs b
    | length xs `mod` b /= 0 = Nothing
    | otherwise = Just (go xs)
    where go [] = []
          go xs = row : go remaining
              where (row, remaining) = splitAt b xs

使用安全类型

确保数据的正确性

矩阵在每行中具有相同数量的元素,而嵌套列表不允许确保这种条件。要确保每行具有相同数量的元素,您可以使用matrixhmatrix

等库

答案 2 :(得分:2)

不,虽然我希望chunksOf在标准库中。如果您愿意,可以从这里获取:https://hackage.haskell.org/package/split-0.2.3.2/docs/Data-List-Split.html

注意更好的定义是:

listToMatrix xs b = (take b xs) : (listToMatrix (drop b xs) b)

虽然这可能会编译到同一个核心。您也可以使用splitAt,但这可能会执行相同的操作。

listToMatrix xs b = let (xs,xss) = splitAt b xs in xs : listToMatrix xss

答案 3 :(得分:0)

你可以使用它,想法是采取主列表的所有块,这是一种不同的方法,但基本上做同样的事情:

Prelude> let list2Matrix n xs = map (\(x ,y)-> (take n) $ (drop (n*x)) y) $ zip [0..] $ replicate (div (length xs)  n) xs
Prelude> list2Matrix 10 [1..100]
[[1,2,3,4,5,6,7,8,9,10],[11,12,13,14,15,16,17,18,19,20],[21,22,23,24,25,26,27,28,29,30],[31,32,33,34,35,36,37,38,39,40],[41,42,43,44,45,46,47,48,49,50],[51,52,53,54,55,56,57,58,59,60],[61,62,63,64,65,66,67,68,69,70],[71,72,73,74,75,76,77,78,79,80],[81,82,83,84,85,86,87,88,89,90],[91,92,93,94,95,96,97,98,99,100]]