【问题标题】:Can I avoid if-elsif-else in this ruby function?我可以在这个 ruby​​ 函数中避免 if-elsif-else 吗?
【发布时间】:2025-12-07 08:40:02
【问题描述】:

我有三个值(foo、bar、bad),我根据哪个值传递给一个函数,我想使用另外两个。

例如,调用self.method(foo) 会导致类似foo 未定义的情况。

def method
  self.foo = 180 - self.bar - self.bad
end

我可以通过简单的 if-elsif-else 设置来做到这一点,但有没有更好(更惯用)的方法?

为清晰起见更新:

以下是生产中的一个建议:

def solve_angles(missing)
  angles = 180 - [ A, B, C ].reject { |e| e == missing }.inject(:+)
end

可以通过@triangle.solve_angles(self.C) 甚至'@triangle.solve_angles("C") 调用。

【问题讨论】:

  • 这完全取决于;决策是基于价值的,还是完全依赖于符号?
  • 展示你到目前为止所写的内容总是有帮助的,而不是让我们其他人尝试可视化你的代码并改进它。
  • 我已经更新了代码,希望能更清楚地理解它。它本质上是在尝试求解三个角,其中两个已知而第三个缺失,但所有三个角的总和必须为 180。

标签: ruby if-statement control-flow


【解决方案1】:

您无需指定要求解的角度;它隐含在问题的定义中。如果你从这样的事情开始(任何类似于错误处理的东西都被省略了):

class Triangle
  def initialize h
    h.keys.each { |key| instance_variable_set "@#{key}".to_sym, h[key] }
  end

  def to_s
    "a=#{@a}, b=#{@b}, c=#{@c}"
  end

  def solve
    angle = instance_variables.inject(180) { |v, a| v -= instance_variable_get(a) }
    [:@a, :@b, :@c].each {|s| instance_variable_set(s, angle) unless instance_variable_defined? s }
    self
  end
end

然后:

pry(main)> t = Triangle.new :a => 20, :c => 30
=> a=20, b=, c=30
pry(main)> t.solve
=> a=20, b=130, c=30
pry(main)> 

如有必要,您还可以返回/指示实际求解的角度。

这实际上并没有避免 if 声明,这是您的具体问题。它消除了明确拼写每个问题的需要,我认为这是问题的意图。

如果你真的需要“解决”,你可以添加:

def solve_for sym
  solve
  instance_variable_get("@#{sym}".to_sym)
end

从技术上讲,您只能在确定未设置值后才能解决,但是嗯。

> t = Triangle.new :a => 20, :c => 30
=> a=20, b=, c=30
> t.solve_for :b
=> 130
> t
=> a=20, b=130, c=30
> t = Triangle.new :a => 20, :c => 30
=> a=20, b=, c=30
> t.solve_for :a
=> 20
> t
=> a=20, b=130, c=30

【讨论】:

  • 这里有一些东西我不太明白它们是如何工作的。感谢这一切。我会玩弄它以确保我理解它是如何工作的。感谢您为此付出的时间。
【解决方案2】:

试试这样的:

def method(arg)
  [ bar, bad, foo ].reject { |e| e == arg }.inject(:+)
end

请注意,您不要对self 接收者,这是隐含的。

【讨论】:

  • 我在上面添加了一些说明。我认为我的做法是错误的。
【解决方案3】:

如果只是基本的加法、乘法等,那么以下就足够了,但不能保证更复杂的。

def method(input)
  self.foo + self.bar + self.bad - input
end

或者,您可以将它们全部放在一个数组中并删除您提供的那个。

def method(input)
  [self.foo, self.bar, self.bad].reject { |e| e==input }.inject { ... }
end

【讨论】:

  • 注意delete返回被删除的元素(或nil),所以第二个sn-p不起作用。
  • 已更新。感谢您了解这一点。
【解决方案4】:

这是我想出的解决方案:

def solve_angles(missing)
      holder = [:A, :B, :C]
      holder.delete(missing)
      angle = 180 - (self.send(holder[0]) + self.send(holder[1]))
end

这是一个糟糕的解决方案吗?为什么?

或者,这是否更清洁/更好?

def solve_angles(missing)
  holder = [:A, :B, :C] - [missing]
  angle = 180 - send(holder[0]) - send(holder[1])
end

或者:

def solve_angles(missing)
  angles = [:A, :B, :C] - [missing]
  angles.inject(180) { |memo, a| memo - send(a) }
end

【讨论】:

  • 我不会称它为“坏”,如果您在它们上面都有attr_accessors,似乎它可以正常工作。将编辑附加到您的答案。
  • 我倾向于更通用的解决方案(而不是明确的三个方面)的一个原因是它适用于其他形状,可以在运行时定义、填充、查询等。例如,s = Shape.new(sides: 5) 可以完成创建五边形所需的所有设置,包括计算内角总和、创建角度访问器等。