【问题标题】:How to use Ruby array in this case?在这种情况下如何使用 Ruby 数组?
【发布时间】:2018-12-13 07:45:15
【问题描述】:

这是一个 ruby​​ 数组:

array = ['x', 3, 0, 4, 4, 7]

我想通过array 进行映射,将数组中的每个整数(0 除外)都减去 2,然后返回一个新数组。

当有字母时,字母不会改变。

示例输出:

['x', 1, 0, 2, 2, 5]

这就是我所拥有的,但我收到错误消息“未定义的方法整数?”有人可以告诉我是什么问题吗?

    def minusNumber(array)
     array.map do |e|
      if e.integer? && e !== 0
      e - 2
      end
     end
    end

【问题讨论】:

  • 使用is_a? 来检查对象的类,因为integer? 不是字符串的方法,这就是NoMethodError 的原因。也许你需要一个 else 语句。并替换!==
  • 您没有为非字母Strings 指定“0”或“2”应该返回什么?它们不是Integers,但它们也不是“字母”。 (为了这个问题,我假设标点符号和其他非数字/非字母字符应被视为“字母”)

标签: arrays ruby


【解决方案1】:

此处的其他答案适用于您当前的输入。比如:

def minusNumber(array)
  array.map do |e|
    if e.is_a?(Integer) && e != 0
      e - 2
    else
      e
    end
  end
end

但这里有一个更灵活的解决方案。这对你现在所处的位置来说可能有点太高级了,但是所有的学习都是好的学习:-)

Ruby 是一种允许在其变量中使用多态性的语言。您可以看到,使用您的方法的示例输入,变量e 可能包含String 对象或Integer 对象。但它实际上可能包含 任何 类型的对象,Ruby 一点也不关心,除非它在使用该变量时遇到错误。

所以。在您的示例中,您需要在输出中保留Integers。但是,如果将来您需要传入一个包含一些 Score 对象的数组,并且您的输出中也需要这些对象怎么办?这是一门全新的课程,你甚至还没有写过,但你知道你以后会写的。有一种方法可以重写你的方法来预测这个未来的类,以及你有一天可能会编写的所有其他Integer-type 类。

不要使用#is_a? 来检查对象的类型,而是使用#respond_to? 来检查它实现了哪些方法。

任何可以用作整数should implement the #to_int method 的类。 Integer 肯定会,你未来的Score 会,但String 不会。任何其他不能被视为整数的类也没有。所以这适用于所有类型的值,正确区分那些响应#to_int 和不响应的值。

def minusNumber(array)
  array.map do |e|
    if e.respond_to?(:to_int) && e != 0
      e - 2
    else
      e
    end
  end
end

同样,这些东西可能有点高级,但最好早日养成根据方法而不是类型来考虑变量的习惯。这种思维方式以后真的会帮到你。

【讨论】:

  • 要求:to_int 是不够的。 e 也需要回复 :-。对于这个任务,询问 e 响应的位置,无论如何 IMO 都不合适,因为要求说如果 e is 是整数,则转换应该应用,而不是如果它可以变成整数。想象一下有人会猴子补丁String 也有一个to_int 方法....有意义的是测试e.class <= Integer 而不是e.is_a?(Integer)
  • 如果它响应:to_int,它将响应:-。实现#to_int 是该类的编写者与其用户签订的合同,上面写着“在可以使用整数的所有情况下,这都可以被视为整数”。继承是最好的,但不是唯一的,遵守这个契约的方式。
  • @user1934428 为什么你会推荐<= 而不是is_a?is_a? 具有更简单的实现,并在此上下文中提供所需的结果。例如OP 不需要知道Integer 是否小于e.class (false) 或根本不在祖先链中(nil) 并且考虑到e 已经是一个实例e.is_a?更符合逻辑,因为来自is_a?<= 的期望结果是对class_search_ancestor 的相同调用,没有<= 实现的所有其他上下文检查
  • @engineersmnky: :to_i 绝对不是要检查回复的消息; String 实现 :to_i,并且用例特别声明按原样返回字符串。有关:to_i:to_int 之间差异的更多信息,请参阅此答案:stackoverflow.com/a/11182123/139299
  • @engineersmnky:我们正在争论一个没有打磨掉边缘案例的规范。我认为我们都是对的,这取决于客户想要什么:-)。我回答的目的是帮助新手开始考虑对象不是基于其类的固定类型,而是基于其公共接口的灵活类型。这是我希望我早点学会的东西。
【解决方案2】:

这是另一种看法:

array = ['x', 3, 0, 4, 4, 7]

transformed = array.map do |e|
  case e
  when 0, String
    e
  when Integer
    e - 2
  else
    fail 'unexpected input'
  end
end

transformed # => ["x", 1, 0, 2, 2, 5]

很遗憾你必须保留你没有减去2的元素。我真的很想做这样的事情

array.grep(Integer).reject(&:zero?).map{|i| i - 2 } # => [1, 2, 2, 5]

还没有找到方法(保留未处理的项目)。

【讨论】:

  • IMO 这么简单的操作,case 语句似乎有点复杂。并不是说这不是一个有效的解决方案并且有效,只是在真正需要检查数字时认为开关是一项昂贵的操作,而其他一切都将被忽略。
  • @ForeverZer0 怎么比较贵?在性能方面,它应该与 if 相同。就脑力而言,它应该更便宜。读起来像英语,无需破译任何is_a?== 0。 ¯\_(ツ)_/¯
  • Switch 语句在几乎所有语言中都比if..end 具有更多的开销,Ruby 也不例外。更不用说相等比较的工作方式不同了,<=> 也比基本相等更昂贵,所以它有多种方式。
  • @ForeverZer0:这里没有<=>,不过
  • @ForeverZer0:不,他们没有。他们使用threequals,而不是宇宙飞船。将我的与您的进行基准比较,是的,您的速度提高了 1.5 倍。不过我觉得这个读起来更好。
【解决方案3】:

您可以在不检查类型的情况下执行此操作。

使用 kind_of?是一种代码异味,表明您的代码是过程性的,而不是面向对象的…… https://www.sandimetz.com/blog/2009/06/12/ruby-case-statements-and-kind-of

def someMethod(arr)
  arr.map do |item|
    next item unless item.to_i.nonzero?
    item - 2
  end
end

someMethod(["x", 3, 0, 4, 4, 7])

> ["x", 1, 0, 2, 2, 5]

【讨论】:

  • 老实说,我会说这是最好的答案,除了 "2".to_i.nonzero? #=> true 但后果是 NoMethodError 因为 String#- 不是一种方法,即使它不太可能接受Integer 作为它的论点
  • item.inspect.to_i.nonzero? 在这种情况下有效。虽然,恕我直言,我认为这并不理想。我对将 item 强制转换为一个字符串并返回一个整数感到不舒服,而且我不认为它为什么或者它在做什么很清楚。
【解决方案4】:

短而甜。

array.map { |v| v.is_a?(Integer) && !v.zero? ? v - 2 : v }

这消除了所有不必要的case 语句、多次迭代和转换。简单地取每个元素,如果它是一个整数并且大于 0,则减去 2,否则什么都不做。

【讨论】:

  • 数组中的每一个整数(0除外),小于0的数会怎样?
  • && v.nonzero?
【解决方案5】:

正如上面评论中提到的,您应该使用is_a? 方法来检查元素的数据类型。下面的代码应该可以按预期工作:

 def minusNumber(array)
   array.map do |e|
     if e.is_a?(String) || e == 0
       e
     else
       e - 2
     end
   end
 end

除了 if 语句,您还可以这样做:

(e.is_a?(String) || e == 0 ) ? e : e-2

请注意,除非您使用map!,否则原始对象不会改变

【讨论】:

    【解决方案6】:

    我认为这是清晰而优雅的:

    array.map { |v| v.is_a?(Integer) && v == 0 ? v : v -2 }
    

    【讨论】:

    • 大声笑,几乎是我已经发布的解决方案的剪切粘贴 stackoverflow.com/questions/51180923/…
    • 并不是说它通常不再重要,尤其是在三元情况下,但一个好的经验法则是条件应该检查​​“最有可能”的场景,以便以最快的方式达到结果. if..elsif 条件尤其如此。因此,鉴于提供的输入,我们可以将结果分为两组 ['x',0][1,3,4,4,7],因为您可以看到第二组更大,因此“更有可能”发生,因此我们应该检查这一点并让第一组代表 false这个案例。但是,您的示例会检查第 1 组。
    猜你喜欢
    • 2014-07-13
    • 2011-08-08
    • 2016-04-11
    • 2022-01-23
    • 2015-11-30
    • 2017-05-16
    • 2012-10-06
    • 2011-05-18
    • 2019-11-29
    相关资源
    最近更新 更多