数组.map与.map中的Ruby整数元素!?以及如何检查哪个代码最好?

Posted

技术标签:

【中文标题】数组.map与.map中的Ruby整数元素!?以及如何检查哪个代码最好?【英文标题】:Ruby integer elements in array .map vs .map!? and how to check which code is best? 【发布时间】:2021-12-15 00:43:30 【问题描述】:

我的任务是输出所有命令行参数的总和,经过一堆错误并意识到 ARGV.to_i 不会将所有元素转换为整数,我想出了以下解决方案。

def argv_sum()
    index = 0
    sum = 0
    ARGV.each do |a|
        ARGV[index] = ARGV[index].to_i
        index += 1
    end
    puts ARGV.sum
end
argv_sum()

def argv_sum()
    index = 0
    sum = 0
    ARGV.each do |a|
        sum += ARGV[index].to_i
        index += 1
    end
    puts sum
end
argv_sum()

然后我在网上查看并意识到我可以将数组中的所有元素转换为整数,并且我假设(如果我错了,请纠正我),以下是该程序的最佳/最有效的代码和将数组的所有元素在一行上转换为整数且不依赖循环的唯一方法。我还假设sum += ARGV[index].to_iARGV[index] = ARGV[index].to_i

def argv_sum()
    ARGV.map!(&:to_i)
    puts ARGV.sum()
end
argv_sum

但我感到困惑的是 mapmap!。我知道前者用新数组替换了数组,后者更改了原始数组。但是,如果我出于某种原因使用map,我将如何区分它和原版。例如以下(与上述几乎相同)不起作用。

def argv_sum()
    ARGV.map(&:to_i) #map is used here instead of map!
    puts ARGV.sum
end
argv_sum

.map 创建了一个新数组,但是我如何专门使用这个新数组或在需要时使用旧数组?因为程序只是假设我使用的是旧数组,所以我会得到一个“字符串不能被强制转换为整数”错误。无论我使用的是ARGV 还是array_test = [] 之类的东西,它都与旧数组具有相同的名称

通过我解决原始总和问题的所有方法,有没有办法检查哪个最有效。 (最适合程序,让程序运行得更高效、更快/节省空间/其他什么?)对于这些程序和我将来可能想要编写的任何其他程序。

【问题讨论】:

【参考方案1】:

.map 创建了一个新数组,但是我如何专门使用这个新数组或在我需要时使用旧数组?

与大多数编程语言(但不是全部)一样,Ruby 有一个叫做variables 的东西。变量提供两件事:它允许您将 label 分配给 thing,并允许您使用标签

分配变量的语法是

variable = expression

从那一刻起,变量variable 引用通过评估expression 返回的对象。所以,你可以这样做:

def argv_sum
  integers = ARGV.map(&:to_i)
  puts integers.sum
end

argv_sum

但是,我认为该变量不会向代码添加任何内容,它并没有真正使代码更易于阅读和/或维护,因此我将摆脱它:

def argv_sum = puts(ARGV.map(&:to_i).sum)

argv_sum

请记住,变量的两个目的是稍后引用一个对象,并给它一个标签。在这种情况下,我们并没有真正稍后再引用该对象,而是立即再引用它,并且标签并没有真正增加代码的清晰度。所以,变量只是多余的绒毛,可以消失。

请注意,正如您在文档中看到的那样,Enumerable#sum 在计算总和之前需要一个块来转换每个元素……换句话说,带有块参数的Enumerable#sum 会融合 mapsum 合为一个操作,所以我们实际上可以这样做

def argv_sum = puts(ARGV.sum(&:to_i))

argv_sum

就我个人而言,我不喜欢混合计算和输入/输出,因为这会使代码更难测试。例如,此代码只能通过将其放入脚本中进行测试,然后编写第二个脚本,该脚本使用不同的命令行参数调用第一个脚本并解析命令行输出。这是复杂、脆弱且容易出错的。如果我们可以通过简单地调用一个方法来测试它会更好。

所以,我将打印和求和部分分开。请注意,该方法的名称已经表明无论如何:argv_sum 听起来该方法返回 ARGV 的总和,但实际上并没有:它返回 nil 并且只有 打印 ARGV 的总和。所以,让我们解决这个问题:

def argv_sum = ARGV.sum(&:to_i)

def print_argv_sum = puts(argv_sum)

print_argv_sum

现在,我们已经将输入/输出部分与计算部分分开了……或者,是吗?不,实际上我们没有:我们已将 打印 部分与计算部分分开,但仍有一些“隐藏”的输入部分:ARGV 本身就是一种“魔法”输入与外部世界,所以我们也应该把它分开:

def sum_array_of_strings(ary) = ary.sum(&:to_i)

def print_array_sum(ary, output_stream = default_output) =
  output_stream.puts(sum_array_of_strings(ary))

def print_argv_sum(output_stream = default_output) =
  print_array_sum(argv, output_stream)

def argv = ARGV
def default_output = $>

print_argv_sum

虽然这对于一个简单的示例来说可能是多余的,但现在这段代码允许我们轻松测试代码的几乎所有方面,而无需任何输入/输出。我们可以通过调用sum_array_of_strings 并将我们选择的数组传递给它来测试求和是否有效。我们可以通过调用print_array_sum 并传递一个我们选择的数组和一个假输出流(例如stringio standard library 中的StringIO 实例)来测试打印是否有效,我们稍后可以检查。我们可以通过覆盖default_output_streamargv 方法以返回我们选择的对象来测试整个逻辑是否正确挂起。

这些都不需要实际传递任何命令行参数或解析打印输出:

#!/usr/bin/env ruby
# frozen_string_literal: true

require 'minitest/autorun'
require 'stringio'

class TestArgvSummer < Minitest::Test
  def setup
    @fake_output = StringIO.new
    @fake_argv = %w[1 2 3 4 5]
  end

  def test_that_sum_array_of_strings_sums_correctly
    assert_equal 0, sum_array_of_strings([])
    assert_equal 42, sum_array_of_strings(%w[42])
    assert_equal 65, sum_array_of_strings(%w[23 42])
    assert_equal 15, sum_array_of_strings(@fake_argv)
  end

  def test_that_print_array_sum_works_correctly
    @fake_output.rewind
    print_array_sum(@fake_argv, @fake_output)
    assert_equal "15\n", @fake_output.string
  end
end

【讨论】:

def method_name(args) = expression 语法是什么? 我相信这是在 Ruby 3 中添加的 @KelvinLawrence 你说得对,Ruby 充满了惊喜。【参考方案2】:

在 ARGV 上使用 Array#sum

您没有显示您的输入或如何调用您的代码,因此很难提供适合您特定用例的建议。然而,在更一般的意义上,Ruby 有很多内置方法可以让整数值相加非常简单。

ARGV 是一个特殊的Array,它将参数作为字符串传递到您的程序中,一旦将它们转换为整数、浮点数或任何#responds_to? :+ 时,您就可以使用Array#sum 将它们全部加起来。例如,考虑以下 Ruby 脚本:

#!/usr/bin/env ruby

# call #to_i on each each element of ARGV,
# sum all the elements, and output the
# result
puts ARGV.map(&:to_i).sum

你可以从你的 shell 中调用这个程序:

$ ruby sum.rb 5 10 15
30

也没有什么可以阻止您在 irb 或 pry 中直接分配给 ARGV。请记住,ARGV always 在来自命令行时包含字符串值,因此当您不直接将其分配为非字符串时,您必须执行从字符串到数字的转换在 REPL 中。

【讨论】:

以上是关于数组.map与.map中的Ruby整数元素!?以及如何检查哪个代码最好?的主要内容,如果未能解决你的问题,请参考以下文章

Scala笔记整理:Scala数据结构—数组map与tuple

js函数之map与reduce

Ruby数组与哈希测试中的堆栈级别太深

LeetCode哈希表#4梦开始的地方:两数之和(map),以及关于容器map的一些代码技巧

数组map方法与如何使用ES5实现

5437.不同整数的最少数目