Haskell中如何实现coupleUp1生成全元素配对列表?
coupleUp1 to Generate the Cartesian Product Let's break down what's going wrong with your current coupleUp1 and how to fix it to match the behavior of coupleUp2 and coupleUp3.
First, let's recap what your working functions are doing: coupleUp3 (do-notation) and coupleUp2 (chained >>=) both generate the cartesian product of the list with itself. For [1..3], that's every possible pair (y,z) where y and z are elements from the input list—exactly the full set of pairs you're expecting.
The list monad's >>= operator works by applying the given function to every element in the input list, then concatenating all the resulting lists. For coupleUp2:
x >>= (\y -> ...)takes eachyfromx- Inside that,
x >>= (\z -> return (y,z))takes eachzfromxand wraps(y,z)into a singleton list (sincereturnfor lists is\a -> [a]) - The outer
>>=concatenates all these singleton lists into one big list of pairs.
What's Wrong With Your Current coupleUp1?
Your current implementation:
coupleUp1 :: [a] -> [(a, a)] coupleUp1 x = case x of [] -> [] (y:ys) -> case ys of [] -> [] (z:zs) -> (y, z):coupleUp1 ys
Only pairs each element y with elements from the tail ys (not the entire original list), and stops early when ys is empty. This is why you're not getting the full set of pairs—it's not iterating over all possible z values for each y.
Correct Implementation Using Case Expressions
To replicate the cartesian product behavior, we need two key steps for each element y in the input list:
- Pair
ywith every element in the original listx(not just the tail) - Concatenate those pairs with the result of processing the rest of the list (
ys)
Here are two clean ways to implement this with case expressions:
Option 1: With a Helper Function (Most Readable)
First, write a helper that pairs a single element with every element in a list:
pairWith :: a -> [a] -> [(a, a)] pairWith y xs = case xs of [] -> [] (z:zs) -> (y, z) : pairWith y zs
Then use this helper in coupleUp1:
coupleUp1 :: [a] -> [(a, a)] coupleUp1 x = case x of [] -> [] (y:ys) -> pairWith y x ++ coupleUp1 ys
For [1..3], this works because:
pairWith 1 [1,2,3]gives[(1,1), (1,2), (1,3)]pairWith 2 [1,2,3]gives[(2,1), (2,2), (2,3)]pairWith 3 [1,2,3]gives[(3,1), (3,2), (3,3)]- The
++operator concatenates all these lists into the full cartesian product.
Option 2: Inline Without Helper Functions
If you want to avoid a separate helper, you can inline the pairing logic using a local let binding:
coupleUp1 :: [a] -> [(a, a)] coupleUp1 x = case x of [] -> [] (y:ys) -> let pairAllWithY xs = case xs of [] -> [] (z:zs) -> (y, z) : pairAllWithY zs in pairAllWithY x ++ coupleUp1 ys
This does exactly the same thing as the helper version—just with the pairing logic defined locally inside the (y:ys) case.
Verifying the Behavior
Testing coupleUp1 [1..3] will now return:
[(1,1),(1,2),(1,3),(2,1),(2,2),(2,3),(3,1),(3,2),(3,3)]
Which matches the output of coupleUp2 and coupleUp3 perfectly.
内容的提问来源于stack exchange,提问作者Agustín Nieto García

