简体   繁体   中英

Couldn't match expected type `(t1, t2, t0)' with actual type `[a0]`

I'm trying to solve a fractional knapsack problem with the input,

[("label 1", value, weight), ("label 2", value, weight), ...]

and the output,

[("label 1", value, solution_weight), ("label 2", value, solution_weight), ...]

but I keep getting the error in computeKnapsack :

"Couldn't match expected type `(t1, t2, t0)' with actual type `[a0]`"

I can't understand what the problem might be. I'm trying to create a list of 3-entry tuples. How can I get it to stop expecting a single 3-entry tuple?

fst3 (a,b,c) = a
snd3 (a,b,c) = b
trd3 (a,b,c) = c
fst4 (a,b,c,d) = a
snd4 (a,b,c,d) = b
trd4 (a,b,c,d) = c
qud4 (a,b,c,d) = d

sortFrac (a1, b1, c1, d1) (a2, b2, c2, d2)
  | d1 > d2 = GT
  | d1 <= d2 = LT

fracKnap (x:xs) =
    fractionalKnapsack (x:xs) []

fractionalKnapsack (x:xs) fracList =
    if length (x:xs) <= 1
        then computeKnapsack (sortBy sortFrac (((fst3 x),(snd3 x),(trd3 x),(snd3 x) / (trd3 x)):fracList)) 100
        else fractionalKnapsack xs (((fst3 x),(snd3 x),(trd3 x),(snd3 x) / (trd3 x)):fracList)

computeKnapsack (x:xs) weightLeft =
    if length (x:xs) <= 1
        then (fst4 x, snd4 x, ((floor (weightLeft / (qud4 x)))*(qud4 x)))
        else (fst4 x, snd4 x, ((floor (weightLeft / (qud4 x)))*(qud4 x))):(computeKnapsack xs (weightLeft-(floor (weightLeft / (qud4 x))*(qud4 x))))

In the then branch of computeKnapsack the result is a single 3-tuple, but in the else branch it is a list of 3-tuples.


I'm going to rewrite computeKnapsack for you. I'll start with your version with the error fixed:

computeKnapsack (x:xs) weightLeft =
    if length (x:xs) <= 1
        then [(fst4 x, snd4 x, ((floor (weightLeft / (qud4 x)))*(qud4 x)))]
        else  (fst4 x, snd4 x, ((floor (weightLeft / (qud4 x)))*(qud4 x))) : (computeKnapsack xs (weightLeft-(floor (weightLeft / (qud4 x))*(qud4 x))))

First, I'm going to say what happens if the first argument to computeKnapsack is the empty list:

computeKnapsack []     _          = []
computeKnapsack (x:xs) weightLeft =
    (fst4 x, snd4 x, ((floor (weightLeft / (qud4 x)))*(qud4 x))) : (computeKnapsack xs (weightLeft-(floor (weightLeft / (qud4 x))*(qud4 x))))

It enables us to get rid of the if -test, making the code shorter overall and easier to understand.

Next, I'll deconstruct x :

computeKnapsack []             _          = []
computeKnapsack ((a,b,_,d):xs) weightLeft =
    (a, b, ((floor (weightLeft / d))*d)) : (computeKnapsack xs (weightLeft-(floor (weightLeft / d)*d)))

You might prefer to follow leftaroundabout's suggestion to create a record type with meaningful names instead. But if you do continue to use a tuple, deconstructing it by pattern matching is much clearer than using your fst4 , snd4 etc functions.

Again, the code is now shorter and easier to understand, though it might help if we used more meaningful names than a , b , and d .

We can continue in this vein:

computeKnapsack []             _          = []
computeKnapsack ((a,b,_,d):xs) weightLeft = (a, b, weight) : (computeKnapsack xs (weightLeft - weight))
  where weight = floor (weightLeft / d) * d

Here I've spotted that the same value was being calculated in two different places, and extracted that value into its own named variable. weight only needs to be calculated once instead of twice, so computeKnapsack is now marginally more efficient. More importantly, I now understand what computeKnapsack is doing.

I understand that you're new to Haskell. Please take this as constructive suggestions on how you can write clearer Haskell code.

I've taken the liberty of cleaning up your computeKnapsack function so you can more clearly see the problem:

computeKnapsack (x:xs) weightLeft =
 let e = (fst4 x, snd4 x, floor (weightLeft / qud4 x) * qud4 x)
  in if length (x:xs) <= 1
     then e
     else e:computeKnapsack xs (weightLeft - floor (weightLeft / qud4 x) * qud4 x)

My changes were purely cosmetic. Even after my changes your computeKnapsack function is still broken, a bad algorithm, and bad coding style.

You can see from the rewritten version that your if statement has two branches, one returns a tuple, and the other returns a list of tuples. You can fix it simply by changing the first branch to return a list with a single element:

computeKnapsack (x:xs) weightLeft =
 let e = (fst4 x, snd4 x, floor (weightLeft / qud4 x) * qud4 x)
  in if length (x:xs) <= 1
     then [e]
     else e:computeKnapsack xs (weightLeft - floor (weightLeft / qud4 x) * qud4 x)

Also, before you submit a third question to StackOverflow, please take the time to complete an introductory tutorial to Haskell like http://learnyouahaskell.com/ which will help fix a lot of your problems.

One problem that throws that error in in the then of fractionalKnapsack . In that line you call function computeKnapsack with a tuple as the input instead of a list .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM