在Coffeescript中,switch case语句中的范围

5

我正在使用Handlebar在我的Rails 3.2 jquery移动应用程序中。

我试图在Coffeescript方法中编写一个switch case语句,例如:

Handlebars.registerHelper 'status', (blog) ->
  switch(parseInt(blog.status))
    when [0..20]
      status = "active"
    when [20..40]
      status = "Moderately Active"
    when [40..60]
      status = "Very Active"
    when [60..100]
      status = "Hyper Active"
  return status

我没有得到任何结果。如何在when语句中使用range?请给出建议。


2
为什么会起作用?这个Github问题似乎表明这种语法没有被接受。 - Quentin Pradet
2个回答

16
你的switch不起作用,正如Cygal在评论中指出的那样(即请参见问题1383)。switch只是一个华丽的if(a == b)结构,你需要能够像这样说:
a = [1,2,3]
switch a
...

当您在数组上使用switch时,希望它能够正常工作。CoffeeScript的设计者认为添加一个(脆弱的)特例来处理数组(这就是全部[a..b]的含义)并不值得。

您可以使用if来实现:

Handlebars.registerHelper 'status', (blog) ->
  status = parseInt(blog.status, 10)
  if 0 <= status <= 20
    'Active'
  else if 20 < status <= 40
    'Moderately Active'
  else if 40 < status <= 60
    'Very Active'
  else if 60 < status <= 100
    'Hyper Active'
  else
    # You need to figure out what to say here

或者像这样使用短路返回return

Handlebars.registerHelper 'status', (blog) ->
  status = parseInt(blog.status, 10)
  return 'Something...'      if status <=   0
  return 'Active'            if status <=  20
  return 'Moderately Active' if status <=  40
  return 'Very Active'       if status <=  60
  return 'Hyper Active'      if status <= 100
  return 'Something else'    # This return isn't necessary but I like the symmetry

请注意,您需要添加三个特殊情况的字符串:
  1. status < 0
  2. status > 100
  3. statusNaN。这种情况通常会落入最终的“它不小于或等于100”的分支中,因为对于所有的 nNaN => nNaN <= n 都是 false。
是的,您可以确定状态始终在假定的范围内。另一方面,在软件中经常发生不可能的事情(因此有了 comp.risks 邮件列表),没有充分的理由留下那些很容易填补的漏洞。
还要注意将基数参数添加到 parseInt 调用中,您不希望前导零弄乱事情。是的,基数参数是可选的,但它真的不应该是这样,您的手指应该自动为您的每个 parseInt 调用添加 , 10

1
这是一个好答案:D,我喜欢第二种解决方案的简洁性,也喜欢第一种解决方案的"all-is-an-expressionability"。我可以建议将两种方式结合起来使用 swtich <nothing> 表达式吗? =D(我不想发布一个新的回答来窃取任何积分) - epidemian
@epidemian:对我来说,那看起来是个不错的选择,我有很多分数,也不介意分享,所以你应该把它作为一个答案写下来。我可能会使用第二个,我喜欢“它是一个内联查找表”的效果。 - mu is too short
好的,我把答案作为你的附录添加了进去。 - epidemian
软件经常会出现故障,但我倾向于在所有编程语言中遵循这个 Erlang 规则:不要进行防御性编程。防御性编程会使调试变得更加困难。 - Quentin Pradet
@Cygal:我没有在那个列表中看到强制的“这取决于”规则 :) 我倾向于采用“大声失败并确保有人知道”的方法,但在浏览器中实现第二部分比较困难。 - mu is too short
当然,如果我理解正确的话,重点是要大声失败。顺便说一下,可以将异常报告回服务器。 - Quentin Pradet

8
mu is too short's answer的基础上,您可以将其第二个代码片段转换为switch表达式:
Handlebars.registerHelper 'status', (blog) ->
  status = parseInt(blog.status, 10)
  switch
    when status <= 0   then 'Something...'      
    when status <= 20  then 'Active'
    when status <= 40  then 'Moderately Active'
    when status <= 60  then 'Very Active'
    when status <= 100 then 'Hyper Active'
    else 'Something else'

这基本上相当于在JavaScript中执行switch(true)(尽管CS编译器将生成一个switch(false)语句,用于对表达式的布尔结果进行否定条件以确保布尔结果...我想是这样的)。

而 switch 中使用范围并不起作用的原因是,CS 中的范围字面量表示普通的 JS 数组(尽管编译器在处理类似于 for i in [1..something] 的代码时会进行一些优化技巧),因此当它们出现在 switch 中时,它们被视为普通的数组值: 如同正常的数组值。
// Generated JS for question's CS code:
switch (parseInt(blog.status)) {
  case [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]:
    status = "active";
    break;
  case [20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40]:
    status = "Moderately Active";
    break;
  case [40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60]:
    status = "Very Active";
    break;
  case (function() {
      _results = [];
      for (_i = 60; _i <= 100; _i++){ _results.push(_i); }
      return _results;
    }).apply(this):
    status = "Hyper Active";
}

switch语句中,其内部的值会与每个case值进行比较,使用的是===,这只适用于基本类型,而不适用于数组(即使它适用于数组,也只是测试数组是否相等,而不是测试switch的值是否包含在case数组中)。

1
看,这是值得做的。我没有想到那种形式的 switch,但下次我会记住的。我认为你是对的,但布尔上下文和使用 switch(false) 而不是 switch(true) - mu is too short

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接