开发者

What is wrong with this Ruby CASE statement?

I have the following ruby function, and when I call it `deg_to_dir 270' the case statement never matches, when it should return W. I have been pulling my hair out and short of converting the entire thing to IF statements I don't have a next step.

def deg_to_dir deg
  # used logic from http://www.csgnetwork.com/degrees2direct.html
  d = deg.to_f
  dir = "#{d}°"
  case d
    when d >=  0    && d <=  11.25,
         d > 348.75 && d <= 360
      dir = "N"
    when d >  11.25 && d <=  33.75
      dir = "NNE"
    when d >  33.75 && d <=  56.25
      dir = "NE"

    when d >  56.25 && d <=  78.75
      dir = "ENE"
    when d >  78.75 && d <= 101.25
      dir = "E"
    when d > 101.25 && d <= 123.75
      dir = "ESE"

    when d > 123.75 && d <= 146.25
      dir = "SE"
    when d > 146.25 && d <= 168.75
      dir = "SSE"
    when d > 168.75 && d <= 191.25
      dir = "S"
    when d > 191.25 && d <= 213.75
      dir = "SSW"
    when d > 213.75 && d <= 236.25
      dir = "SW"

    when d > 236.25 && d <= 258.75
      dir = "WSW"
    when d > 258.75 && d <= 281.25
      dir = "W"
    when d > 281.25 && d <= 303.75
      dir = "WNW"
    when d > 303.75 &开发者_JAVA百科& d <= 326.25
      dir = "NW"
    when d > 326.25 && d <= 348.75
      dir = "NNW"
  end
  dir
end


What you're trying to do is an if elsif pattern. If you're trying to use a case when pattern should be using ranges:

case d
when (0..11.25)
  dir = "N"
when (11.25..33.75)
  dir = "NE"
  # etc...
end

Every time the parser comes to a when statment it executes case_statement === when_statement. All of your when statements evaluate to a boolean, which will never equal a degree.


I'd say the worst part about that case statement is that it shouldn't be a case statement at all.

DIRS = %w[N NNE NE ENE E ESE SE SSE S SSW SW WSW W WNW NW NNW]

def deg_to_dir deg
  deg = deg.to_f
  dir = DIRS[(deg + 11.25).div(22.5)]
  "#{deg}° #{dir}"
end


If you do not want to compare a value but instead "simulate" an if-else with case (as in your case) you simply have to write

case

instead of

case d


The problem is that you wrote case d and a boolean condition in the when (so it performs a true|false === d, which never succeeds). It should look like this (notice how to take advantage of case being an expression in Ruby):

dir = case 
  when d >=  0    && d <=  11.25,
       d > 348.75 && d <= 360
  "N"
  ...
end

Anyway, why don't you refactor this method without conditionals? IMO this is a pretty poor implementation; an array with the abbrevation names (and code that uses it judiciously) is all you should need. Probably a one/two-liner.


Combinating Kyle's and tokland's answers, you will end up in following:

def deg_to_dir deg
  case deg.to_f
  when (0..11.25)
    "N"
  when (11.25..33.75)
    "NE"
  # ... your rest ...
  end
end

And no helper variables!


Kyle's answer with the ranges is the right one. The reason is that the case statement compares d with the when statement using ===.

In this case you're doing a compare of

270 === true

which isn't. A float isn't going to equal a boolen, for any values of float and bool.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜