haskell 定义一个函数,该函数将给定列表的所有可能排列作为对列表返回

bzzcjhmw  于 2022-12-23  发布在  其他
关注(0)|答案(2)|浏览(237)

我是Haskell的新手,有一个工作表问题把我难住了。这个问题要求我定义一个函数,它接受一个元组列表,每个元组包含一个数字子列表,并返回一个元组列表,每个元组包含一对整数。这是预期的输入和输出。

possibles [([1],[2,3,4]),([1,2],[3,4])] ===> [(1,234),(1,243),(1,342),(1,324),(1,423),(1,432),(12,34),(12,43),(21,34),(21,43)]

我尝试使用“Data.List”库中的“permutations”函数和我制作并附在下面的另外两个函数。
x一个一个一个一个x一个一个二个x

c2e8gylq

c2e8gylq1#

欢迎来到 haskell 家庭!2这是一个有趣的问题,可以帮助你发现 haskell 的美丽。
首先,我们先看问题,再进入实施:
1.将[2,3,4]转换为[[2,3,4], [2,4,3], [3,2,4], [3,4,2], [4,2,4], [4,3,2]]
1.将[2,3,4]转换为234(您已经实现了这一部分)
1.将([12,21], [34,43])转换为[(12,34),(12,43),(21,34),(21,43)]
1.把所有的东西都组合在一起(组合真的很强大)
1.重构
其次,这里有2个有用的工具,我建议:

现在让我们尝试实现它。
首先,我们知道输入类型是[([Int], [Int])]

-- try out at https://replit.com/languages/haskell 

input :: [([Int], [Int])]
input = [([1],[2,3,4]),([1,2],[3,4])]

main = print input

现在让我们按照上面提到的步骤。
步骤1称为permutation :: [a] -> [[a]],它不仅适用于Int,还适用于任何类型的列表。
如果你在hoogle中搜索[a] -> [[a]],你可能会发现这个函数,现在回到REPL:

import Data.List (permutations)

input :: [([Int], [Int])]
input = [([1],[2,3,4]),([1,2],[3,4])]

step1 :: [a] -> [[a]]
-- challenge yourself and see if you can implement it
-- feel free to look at the source code for the function in hoogle
step1 = permutations

main = print $ step1 [2,3,4]
-- result: [[2,3,4],[3,2,4],[4,3,2],[3,4,2],[4,2,3],[2,4,3]]

对于步骤2,我们现在使用您的代码:

import Data.List (permutations)

input :: [([Int], [Int])]
input = [([1],[2,3,4]),([1,2],[3,4])]

step1 :: [a] -> [[a]]
-- challenge yourself and see if you can implement it
-- feel free to look at the source code for the function in hoogle
step1 = permutations

step2 :: [Int] -> Int
step2 digits = foldl (\acc x -> acc * 10 + x) 0 digits
-- or try the shorter version
-- step2 = foldl (\acc x -> acc * 10 + x) 0

main = print $ step2 [2,3,4]
-- result: 234

步骤3是使用list comprehension的一个很好的示例:

import Data.List (permutations)

input :: [([Int], [Int])]
input = [([1],[2,3,4]),([1,2],[3,4])]

step1 :: [a] -> [[a]]
-- challenge yourself and see if you can implement it
-- feel free to look at the source code for the function in hoogle
step1 = permutations

step2 :: [Int] -> Int
step2 digits = foldl (\acc x -> acc * 10 + x) 0 digits

step3 :: ([a], [b]) -> [(a, b)]
step3 (xs, ys) = [(x,y) | x <- xs, y <- ys]

main = print $ step3 ([12,21], [34,43]) 
-- result: [(12,34),(12,43),(21,34),(21,43)]

现在是令人兴奋的部分--合成。首先我们将step1step2结合起来:

import Data.List (permutations)

input :: [([Int], [Int])]
input = [([1],[2,3,4]),([1,2],[3,4])]

step1 :: [a] -> [[a]]
-- challenge yourself and see if you can implement it
-- feel free to look at the source code for the function in hoogle
step1 = permutations

step2 :: [Int] -> Int
step2 digits = foldl (\acc x -> acc * 10 + x) 0 digits

step3 :: ([a], [b]) -> [(a, b)]
step3 (xs, ys) = [(x,y) | x <- xs, y <- ys]

step12 :: [Int] -> [Int]
step12 xs = map step2 (step1 xs)
-- step12 = map step2 . step1
    

main = print $ step12 [2, 3, 4]
-- result: [234,324,432,342,423,243]

注意,你可以专注于函数组合,而不必关心这里的值:

step12 :: [Int] -> [Int]
step12 = map step2 . step1

现在,我们将step3step12组合在一起

import Data.List (permutations)

input :: [([Int], [Int])]
input = [([1],[2,3,4]),([1,2],[3,4])]

step1 :: [a] -> [[a]]
-- challenge yourself and see if you can implement it
-- feel free to look at the source code for the function in hoogle
step1 = permutations

step2 :: [Int] -> Int
step2 digits = foldl (\acc x -> acc * 10 + x) 0 digits

step3 :: ([a], [b]) -> [(a, b)]
step3 (xs, ys) = [(x,y) | x <- xs, y <- ys]

step12 :: [Int] -> [Int]
step12 xs = map step2 (step1 xs)
-- step12 = map step2 . step1

step123 :: ([Int], [Int]) -> [(Int, Int)]
step123 (xs, ys) = step3 (step12 xs, step12 ys)

main = print $ step123 ([1,2],[3,4])
-- result: [(12,34),(12,43),(21,34),(21,43)]

现在最后一步是一个简单的mapfmap
让我们看看目前为止我们得到了什么:

import Data.List (permutations)

input :: [([Int], [Int])]
input = [([1],[2,3,4]),([1,2],[3,4])]

step1 :: [a] -> [[a]]
-- challenge yourself and see if you can implement it
-- feel free to look at the source code for the function in hoogle
step1 = permutations

step2 :: [Int] -> Int
step2 digits = foldl (\acc x -> acc * 10 + x) 0 digits

step3 :: ([a], [b]) -> [(a, b)]
step3 (xs, ys) = [(x,y) | x <- xs, y <- ys]

step12 :: [Int] -> [Int]
step12 xs = map step2 (step1 xs)
-- step12 = map step2 . step1

step123 :: ([Int], [Int]) -> [(Int, Int)]
step123 (xs, ys) = step3 (step12 xs, step12 ys)

main = print $ map step123 input
-- result: [[(1,234),(1,324),(1,432),(1,342),(1,423),(1,243)],[(12,34),(12,43),(21,34),(21,43)]]

现在我们通过(map step123 input)得到了[[(Int, Int)]],我们所要做的就是一个函数把[[a]]转换成[a],让我们在hoogle中搜索它(小心选择一个行为正确的)。
因此,让我们将step4放在这里,以完成解决方案的第一个版本:

import Data.List (permutations)

input :: [([Int], [Int])]
input = [([1],[2,3,4]),([1,2],[3,4])]

step1 :: [a] -> [[a]]
-- challenge yourself and see if you can implement it
-- feel free to look at the source code for the function in hoogle
step1 = permutations

step2 :: [Int] -> Int
step2 digits = foldl (\acc x -> acc * 10 + x) 0 digits

step3 :: ([a], [b]) -> [(a, b)]
step3 (xs, ys) = [(x,y) | x <- xs, y <- ys]

step12 :: [Int] -> [Int]
step12 xs = map step2 (step1 xs)
-- step12 = map step2 . step1

step123 :: ([Int], [Int]) -> [(Int, Int)]
step123 (xs, ys) = step3 (step12 xs, step12 ys)

step4 :: [([Int], [Int])] -> [(Int, Int)]
step4 x = concat (map step123 x)
-- step4 = concat . map step123
-- step4 = concatMap step123

main = print $ step4 input
-- result: [(1,234),(1,324),(1,432),(1,342),(1,423),(1,243),(12,34),(12,43),(21,34),(21,43)]

注意,我们可以只关注合成,而不考虑价值:

step4 :: [([Int], [Int])] -> [(Int, Int)]
step4 = concat . map step123
-- alternatively
-- step4 = concatMap step123

现在让我们看看如何使用列表解析重构step123,并将其重命名为getCombinations

import Data.List (permutations)

input :: [([Int], [Int])]
input = [([1],[2,3,4]),([1,2],[3,4])]

toNumber :: [Int] -> Int
toNumber = foldl1 (\x y -> 10*x+y)

-- ([1, 2], [3, 4]) -> [(12, 34), (12, 43), (21, 34), (21, 43)]
getCombinations :: ([Int], [Int]) -> [(Int, Int)]
getCombinations (xs, ys) = [
  (toNumber x, toNumber y) 
  | x <- permutations xs
  , y <- permutations ys]

handleInput :: [([Int], [Int])] -> [(Int, Int)]
handleInput = concatMap getCombinations

main = print . handleInput $ input

这绝不是最简洁或性能最好的代码,但它是一个良好的开端,我希望您能从中学到一些东西。
你有一个很好的开始,继续努力,享受Haskell:)

qlzsbp2j

qlzsbp2j2#

我们可以先将问题简化为:

possibles :: [([Int], [Int])] -> [(Int, Int)]
possibles = concatMap (uncurry possible')

因为输入中的每一项都是单独的case。
对于possible',从xy生成所有排列,然后将它们转换为数字就足够了:

possible' :: [Int] -> [Int] -> [(Int, Int)]
possible' xs ys = [
    (x, number ys')
    | xs' <- permutations xs
    , let x = number xs'
    , ys' <- permutations ys
  ]

它以不同的顺序产生结果:

ghci> possibles [([1],[2,3,4]),([1,2],[3,4])]
[(1,234),(1,324),(1,432),(1,342),(1,423),(1,243),(12,34),(12,43),(21,34),(21,43)]

但是,通过实现不同的置换函数,您可以改变生成项的顺序。
适用于:

possibles [([1],[2,3,4,5,6,7,8,9]),([1,2],[3,4,5,6,7,8,9]),([1,2,3],[4,5,6,7,8,9]),([1,2,3,4],[5,6,7,8,9]),([1,2,3,4,5],[6,7,8,9]),([1,2,3,4,5,6],[7,8,9]),([1,2,3,4,5,6,7],[8,9]),([1,2,3,4,5,6,7,8],[9])]

这将生成 * 1! × 8!+2! × 7!+3! × 6!+4! × 5!+5! × 4!+6! × 3!+7! × 2!+8! × 1!= 2 ×(1! × 8!+2! × 7!+3! × 6!+4! × 5!)= 2 × 57 '600 = 115' 200 *

相关问题