Reputation: 11
The "Guards vs. If-Then-Else" helped a little but I still want to know if this can work somehow. I need to take a list and return every other element of the list. For even length lists I got it just if length (xs) mod
2 == 1 to start is there an issue so I want to break up the initial length of list case like this:
everyOther:: [Int] -> [Int]
everyOther [] = []
everyOther (x:xs) = case length (xs) 'mod' 2 of 0 ->
if (length (xs) `mod` 2 == 0)
then x:everyOther (take 1 xs)
else x:everyOther (drop 1 xs)
1 -> if (length (xs) `mod` 2 == 1)
then x:everyOther (take 1 xs)
else x:everyOther (drop 1 xs)
It's telling me there is a possible "spacing error" at the "if" but is this functionally correct. Can I do this?
Upvotes: 1
Views: 2977
Reputation: 152837
There seem to be a few different errors in this code, varying through the whole range from syntactic to conceptual to algorithmic. Let's start with the syntax errors and move up the chain.
Code in Haskell is organized into blocks. Each element of a block must start at the same indentation level; and for beginners, nested blocks should use a deeper indentation level than its surrounding block. At the top level, modules are a block whose elements are equations. case
/of
also begins a block, whose elements are pattern -> expression
matches.
Together, these rules mean that the patterns 0
and 1
in your case
statement should be aligned with each other and indented deeper than the first e
of everyOther
. The expression inside should be deeper still to avoid confusion. Here's one popular style for satisfying these constraints:
everyOther (x:xs) = case length (xs) 'mod' 2 of
0 -> if (length (xs) `mod` 2 == 0)
then x:everyOther (take 1 xs)
else x:everyOther (drop 1 xs)
1 -> if (length (xs) `mod` 2 == 1)
then x:everyOther (take 1 xs)
else x:everyOther (drop 1 xs)
Next: backticks (`
— usually located to left of the row of numbers at the top of the keyboard) and forward ticks ('
) mean different things in Haskell. It's important to use backticks for converting prefix functions to infix ones; so length (xs) 'mod' 2
should be length (xs) `mod` 2
. You also have a lot of redundant parentheses, notably around your argument to length
and in the coditions of your if
expressions. Though not an error, it is worthwhile to learn the precedence rules, as you'll need to understand code that doesn't litter them everywhere.
everyOther (x:xs) = case length xs `mod` 2 of
0 -> if length xs `mod` 2 == 0
then x:everyOther (take 1 xs)
else x:everyOther (drop 1 xs)
1 -> if length xs `mod` 2 == 1
then x:everyOther (take 1 xs)
else x:everyOther (drop 1 xs)
Next up is a conceptual error. In an expression like case foo of 0 -> a; 1 -> b
, we will only ever evaluate expression a
when foo == 0
, and will only ever evaluate expression b
when foo == 1
. This makes the tests in your conditions redundant; in both branches of the case
, we will definitely take the then
branch. So if we were to take this code literally, it would be simpler and equivalent to write this instead:
everyOther (x:xs) = case length xs `mod` 2 of
0 -> x:everyOther (take 1 xs)
1 -> x:everyOther (take 1 xs)
Since we now have the same code in both branches of the case
, it seems clear this wasn't your intention; but it isn't super clear to me what your intention was.
There is another suspicious (though again, not technically wrong) detail: keep in mind that in the foo
of everyOther (x:xs) = foo
, the list xs
does not include the first element of the list given to everyOther
. So calls like length xs
will be off by one compared to the length of the input given to everyOther
. Take that into account as you write the expressions for the 0
and 1
patterns of your case
statement.
Returning to your intention: since in both patterns you write everyOther (take 1 xs)
, I assume that you wanted that expression to be evaluated sometimes. This makes me think you have an algorithmic error; take 1 xs
will throw away almost all of the input list, which doesn't match my understanding of what you want everyOther
to do.
Hopefully this discussion guides you in improving your attempt, and you can make more progress towards your goals.
Upvotes: 5