简体   繁体   中英

Sorting a part of a list without using library functions

I want to sort the first three elements of a list, yet keep the other elements untouched. And I don't want to use any library functions because I am new to Haskell.

This is what I have so far:

{-# OPTIONS_GHC -Wincomplete-patterns #-}


sort2 (x, y) = if x > y then (y,x) else (x,y)
sort3 (x,y,z)
    | x > y && y > z = (z,y,x)
    | x > z && z > y = (y,z,x)
    | y > x && x > z = (z,x,y)
    | y > z && z > x = (x,z,y)
    | z > x && x > y = (y,x,z)
    | z > y && y > x = (x,y,z)
    | otherwise = (0,0,0)

--sortfirst3 :: [Int] -> [Int]
sortfirst3 (x:y:z:v:xs) =  sort3(x,y,z) : sortfirst3 (v:xs)

I'm trying to sort the first three elements, put them in a list, and then put the other elements in another list and then concatenate them with each other. However, this generates an error because of the type difference a tuple and a list. Is there another way I should be doing this?

You don't need to involve tuples. You can write what you wrote for tuples as patterns to match instead.

sortFirst3 [x, y] = if x > y then [y, x] else [x, y]
sortFirst3 [x, y, z]
    | x >= y && y >= z = [z, y, x]
    | x >= z && z >= y = [y, z, x]
    | y >= x && x >= z = [z, x, y]
    | y >= z && z >= x = [x, z, y]
    | z >= x && x >= y = [y, x, z]
    | z >= y && y >= x = [x, y, z]
sortFirst3 (x : y : z : ws) = sortFirst3 [x, y, z] ++ ws
sortFirst3 xs = xs

The last line catches what has not been matched, which are the singletons and empty lists, which remain the same when sorted. You should not have strict inequalities in your guards since elements may repeat. Also note that, in your code, even if you didn't have the type mismatch, the recursive call to sortFirst3 would be erroneous, since after sorting the first three terms, the first three of the rest would be sorted, and so on.

sortfirst3 (x:y:z:v:xs) =  sort3(x,y,z) : sortfirst3 (v:xs)

This definition says, take the first 4 elements off the list, sort the first 3 into a tuple, then cons it onto the (recursive) result on the fourth element of the list with the rest of the list.

This reveals three changes to be made.

First, we only need to match the first 3 elements, not the first 4, but we should also probably deal with cases for when the list is <3 elements long. I've just made it an error, but you can use eg. sort2 instead.

sortfirst3 (x:y:z:xs) =  sort3 (x,y,z) : sortfirst3 xs
sortfirst3 _ = error "Can't sort first 3 elements of list shorter than 3 elements"

Second, we intend to sort the first 3 elements ie. resulting in a 3 element list. (:) :: a -> [a] -> [a] ie. takes an element (not a list) in the first argument, what we want instead is concatentation (++) :: [a] -> [a] -> [a] .

sortfirst3 (x:y:z:xs) =  sort3 (x,y,z) ++ sortfirst3 xs

Third, we intend to leave the elements after the first 3 untouched, so we shouldn't actually make a recursive call on the rest of the list - otherwise we'll instead be sorting the list in 3 element chunks.

sortfirst3 (x:y:z:xs) =  sort3 (x,y,z) ++ xs

This is now almost correct, but for sort3 , which as you identified incorrectly returns a tuple rather than a 3-element list. kuoytfouy's answer helps with that.

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