简体   繁体   中英

Haskell - check whether a 2D list has the same number of rows as columns

I have a 2D list [[Int]] in Haskell and I want to check two things:

  1. whether the list has the sam number of rows as columns
  2. whether the rows have the sam number of elements

For instance:

[[1,2,3], [1,55,9]] has the same number of rows as columns - here 2 - and each row has the same number of elements namely 3.

But

[[1,2], [1,55], [4,7]] has the same number of elements in each row though it has unequal number of rows and columns namely 3r 2c.

yet another example:

[[1,2], [1,55], [4,7,8]] has neither the same number of rows as columns nor each row has the same number of elements.

Actually step 1 includes step 2, am I right??

My attempt:

So what I attempted so far, is this:

listIsEqual :: [[Int]] -> Bool
listIsEqual myList = (all (\x -> length x == (length myList)) )

Right now I get the following error mesage:

Couldn't match expected type `Bool' with actual type `[a0] -> Bool'
In the return type of a call of `all'
Probable cause: `all' is applied to too few arguments
In the expression: (all (\ x -> length x == (length myList)))
In an equation for `listIsEqual':
            listIsEqual myList = (all (\ x -> length x == (length myList)))

Could anyone tell me where the problem is?

Is there also any other ways to solve this problem?

GHC's error messages aren't always the most helpful, but in this case it got it right.

Probable cause: `all' is applied to too few arguments

And indeed, you forgot the second argument to all :

listIsEqual myList = all (\x -> length x == length myList) myList
                                                           ^^^^^^

For the second task, you can map the length of every row (the number of columns in that row) defining a function

let columnLengths rows = map length rows
Prelude> columnLengths [[1,2], [1,55], [4,7,8]]
[2,2,3]

Now that we have a list containing the lengths of the columns, we have to check whether they are all equal. The function nub in Data.List removes duplicates from a list.

let columnsLengthEqual = (==) 1 . length . nub . columnLengths

Or all together

let columnsLengthEqual = (==) 1 . length . nub . map length

Matrix respecting your criteria, are squared matrix then checking if the square of first 's row's length is equal to the number of element should be ok.

isSquaredMatrix xs@(h:_)  = ((^2) . length $ h) == (length . concat $ xs) 
isSquaredMatrix _         =  True 

But as it has been pointed out by hammar, this is incorrect since we can have positive outcome using wrong input.

# isSquaredMatrix [[1,2,3],[4,5],[6,7,8,9]]
True -- But this is false  

@John,

we use @ into pattern matching when we want to refer to the whole type at the same time we have break it down. An example should give you more insight,

Usually we can define an exhaustive function working on list using pattern matching as follow.

actOnList []     = -- do something when we encounter an empty list
actOnList (x:xs) = -- do something with h, and do another stuff with xs  

For example,

actOnList [] = []
actOnList (x:xs) = 
    if (pred x)
    then x:xs
    else actOnList xs

Here my function consumme the list until a predicate is satisfied.
We can imagine skipUntilMeetAChar

skipUntilMeetAChar :: [Char] -> Char -> [Char]
skipUntilMeetAChar []  c    = []
skipUntilMeetAChar (x:xs) c = 
    if (x==c)
    then x:xs
    else actOnList xs c

As you see when the char is met we'd like to return the list as it, not only the tail, then to do so we need to reconstruct our list using the head x and the tail xs . This can be overcome using @ .

skipUntilMeetAChar :: String -> Char -> String
skipUntilMeetAChar []  c    = []
skipUntilMeetAChar l@(x:xs) c = 
    if (x==c)
    then l 
    else actOnList xs c

Now, regarding ($) operator, this is again some syntactic sugar.
As function application are left associative, this lead us to extensively use bracket to reorder the application of our function, as in the example below.

# f3 (f2 (f1 (f0 x)))

Then to avoid the pain of managing closing parentheses, dollars operator $ have been introduce and then our previous expression become.

# f3 $ f2 $ f1 $ f0 x

Which is definitely more readable and easiest to write.

Note that this operator is defined as follow.

($) :: (a -> b) -> a -> b  
f $ x = f x  

And I advise you to learn more about it consulting the following introduction material .

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