按升序返回所有可能的数字列表,其中第一个元素来自xs,第二个元素来自ys -- Haskell

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

如果有人能帮我完成这项任务,我将不胜感激。
令两个整数列表xs和ys按升序排列。定义一个函数

generate :: [Int] -> [Int] -> [[Int]]

按升序返回所有可能的数字列表,其中第一个元素来自xs,第二个元素来自ys,依此类推。每个列表的最后一个元素必须是ys列表中的数字。
示例:

generate [10, 15, 25] [1, 5, 20, 30] == [[10, 20], [10, 30], [15, 20], [15, 30], [25, 30], [10, 20, 25, 30], [15, 20, 25, 30]]

这是我目前所做的,但并不适用于所有情况。

generate :: [Int] -> [Int] -> [[Int]]
generate [] [] = [[]]
generate xs ys = nub $ twos xs ys ++ processMore xs ys
  where
    twos xs ys = [[x, y] | x <- xs, y <- ys, x < y]
    more [] _ = []
    more _ [] = []
    more (x : xs) (y : ys)
      | x < y = x : y : more (filter (> y) xs) ys
      | otherwise = more (x : xs) ys
    processMore [] _ = []
    processMore xs ys = more xs ys : processMore (tail xs) ys

对于上面的例子,它工作正常,但对于这个:

-- this is what i SHOULD get
generate [1, 5, 8] [2, 6, 67] == [[1, 2], [1, 6], [1, 67], [5, 6], [5, 67], [8, 67], [1, 2, 5, 6, 8, 67], [5, 6, 8, 67], [1, 2, 5, 67], [1, 6, 8, 67], [1, 2, 8, 67], [1, 2, 5, 6]]

-- But this is what i actually get( and the problem comes from the processMore, but don't know how to fix it):
generate [1, 5, 8] [2, 6, 67] -> [[1,2],[1,6],[1,67],[5,6],[5,67],[8,67],[1,2,5,6,8,67],[5,6,8,67],[8,67]] -- this is what i get
ghhaqwfi

ghhaqwfi1#

我还没有发现你代码中的bug,但我认为有一个更好的方法。你可以把问题看作是有对的构建块(在你的代码中是twos函数)。然后,用这些块构建更大的列表,直到你不能再做为止。
例如,

xs = [10, 15, 25] 
ys = [20, 30]
building_blocks = [(10,20), (10,30), (15, 20), (15,30), (25, 30)] -- notice this is a list of pairs not a list of list

-- The first iteration is just the list of building blocks
[[10,20], [10,30], [15, 20], [15,30], [25, 30]]

-- The second iteration we need to take the previous result and add more building blocks. 
-- The adding condition is that the last elem of the list must be less than the head
-- of the building block.

   [[10,20], [10,30], [15,20], [15,30], [25,30]] -- comming from first iter
++ [[10,20,25,30], [15,20,25,30]]                -- from second iteration.

-- We need to continue taking the result of the previous iter and adding building blocks
-- until no more blocks can't be added.
   [[10,20], [10,30], [15,20], [15,30], [25,30]] -- comming from first iter
++ [[10,20,25,30], [15,20,25,30]]                -- from second iteration.
++ []                                            -- from third iter

使用这个透视图我们可以编写代码,

-- This is an auxiliary function which given a list of list
-- and a list of building blocks, it returns the list of lists
-- expanded by the blocks.
addBuildingBlock :: [[Int]] -> [(Int, Int)] -> [[Int]]
addBuildingBlock xss ps = 
  [xs ++ [a,b] | xs <- xss, (a, b) <- ps, last xs < a]

generate :: [Int] -> [Int] -> [[Int]]
generate xs ys = recursive initial_pairs
  where
    -- The building blocks as pairs
    building_blocks = [(x,y) | x <- xs , y <- ys, x < y]
    -- The initial pairs which are the same as the building blocks
    initial_pairs = [[x,y] | x <- xs , y <- ys, x < y]
    -- the recursive call. Keep adding blocks until you can't
    -- left as and exercise
    recursive :: [[Int]] -> [[Int]]
    recursive l = 
     let longer = addBuildingBlock l building_blocks -- The list which is the last iteration elongated by all the building blocks
     in case longer of
          [] -> undefined
          _  -> undefined
dgtucam1

dgtucam12#

这里有一个我喜欢的方法。与其预先扫描两个列表来寻找有效的对,我更喜欢将其视为遍历列表,在每一点选择“我应该包含这个元素,还是跳过它?”因为我们可以跳过它或包含它,所以我使用(<|>)(Alternative中的选择操作符)来指示选择。对于列表,这等效于(++)。但我认为它更好地将算法描述为选择。

generate :: [Int] -> [Int] -> [[Int]]
generate [] _ = []
generate (x : xs) ys = useFirstX <|> skipFirstX
  where skipFirstX = generate xs ys
        useFirstX = chooseY (dropWhile (<= x) ys)
        chooseY [] = []
        chooseY (y : ys) = useFirstY <|> skipFirstY
          where skipFirstY = findY ys
                useFirstY = ([x, y] ++) <$> ([] : generate (dropWhile (<= y) xs) ys)

如果xs为空,我们不能生成任何对;如果它不为空,我们可以跳过第一个x,或者使用它。要使用它,请丢弃所有小于它的ys,然后选择一个。
选择一个y也同样简单,如果ys为空,我们就不能选择一个,所以我们什么也不产生;否则,我们知道ys中的所有元素都大于我们选择的x,所以我们可以自由选择任何y,跳过第一个y很容易:把它扔了继续走。
选择第一个y是这个解决方案中唯一复杂的一行。我们选择了xy,所以我们知道每个解决方案都必须从[x, y]开始。因此,我们将该列表附加到所有递归解决方案中。递归解决方案是什么?好吧,我们可以到此为止([]),或者我们可以继续,在丢弃小于我们选择使用的yxs之后,再次调用generate xs ys

相关问题