简体   繁体   English

如何在Haskell的案例陈述中使用if / then / else或guards?

[英]How do I use if/then/else or guards in a case statement in Haskell?

The "Guards vs. If-Then-Else" helped a little but I still want to know if this can work somehow. “ Guards vs. If-Then-Else”有所帮助,但我仍然想知道这是否可以工作。 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: 对于偶数长度列表,即使长度(xs) mod 2 == 1开始存在问题,我也明白了,所以我想分拆列表大小的初始长度,如下所示:

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. 它告诉我“ if”处可能存在“间距错误”,但这在功能上是正确的。 Can I do this? 我可以这样做吗?

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. Haskell中的代码被组织成块。 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. case / of也开始一个块,其元素为pattern -> expression match。

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 . 这些规则一起意味着, case语句中的模式01应该彼此对齐,并且缩进的程度要比everyOther的第一个e缩进得多。 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. 下一步:反引号( ` -通常位于键盘顶部的数字行的左侧)和正勾号( ' )在Haskell中含义不同。 It's important to use backticks for converting prefix functions to infix ones; 使用反引号将前缀函数转换为infix函数是很重要的。 so length (xs) 'mod' 2 should be length (xs) `mod` 2 . 所以length (xs) 'mod' 2 length (xs) `mod` 2应该是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. 您也有很多多余的括号,特别是关于length的论点和if表达式的代码。 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 在诸如case foo of 0 -> a; 1 -> b这样的表达式中case foo of 0 -> a; 1 -> b 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 . case foo of 0 -> a; 1 -> b ,我们将仅在foo == 0时评估表达式a ,并且仅在foo == 1时评估表达式b This makes the tests in your conditions redundant; 这样可以使您的条件下的测试变得多余。 in both branches of the case , we will definitely take the then branch. case两个分支中,我们肯定会采用then分支。 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; 由于我们现在在case两个分支中都具有相同的代码,因此显然这不是您的意图; 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 . 还有另一个可疑的(尽管同样,从技术上来讲不是错误的 )细节:请记住,在everyOther (x:xs) = foofoo everyOther (x:xs) = foo ,列表xs不包括提供给everyOther的列表的第一个元素。 So calls like length xs will be off by one compared to the length of the input given to everyOther . 因此,与给everyOther的输入长度相比,像length xs类的调用将减少everyOther Take that into account as you write the expressions for the 0 and 1 patterns of your case statement. 在编写case语句的01模式的表达式时要考虑到这一点。

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. 回到您的意图:由于在两种模式下您都编写了everyOther (take 1 xs) ,因此我假设您希望有时对该表达式进行求值。 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. take 1 xs将丢弃几乎所有输入列表,这与我对您要everyOther要做的理解everyOther

Hopefully this discussion guides you in improving your attempt, and you can make more progress towards your goals. 希望本讨论会指导您改进尝试,并且可以在实现目标方面取得更大的进步。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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