【问题标题】:Understanding grep within Enumerable module了解 Enumerable 模块中的 grep
【发布时间】:2021-05-21 01:13:44
【问题描述】:

非常感谢在我的第一篇堆栈溢出帖子中提供的任何帮助!

我通常很困惑,为什么每次尝试运行我的代码时都会收到空数组。 我正在创建一个过滤性别“M”并返回其元素的方法。

我确信有多种方法可以成功运行此代码,但我对使用 grep 及其功能很感兴趣。这是一条捷径,我认为学习它会很好。再次感谢您。

students = [
    {name: 'John', grade: 8, gender: 'M'},
    {name: 'Sarah', grade: 12, gender: 'F'},
    {name: 'Bob', grade: 16, gender: 'M'},
    {name: 'Johnny', grade: 2, gender: 'M'},
    {name: 'Ethan', grade: 4, gender: 'M'},
    {name: 'Paula', grade: 8, gender: 'F'},
    {name: 'Donald', grade: 5, gender: 'M'},
    {name: 'Jennifer', grade: 13, gender: 'F'},
    {name: 'Courtney', grade: 15, gender: 'F'},
    {name: 'Jane', grade: 9, gender: 'F'}
]
def is_male(gender)
  gender.grep(/M/) { |gend| gend[:gender] }
end
p is_male(students)

【问题讨论】:

    标签: ruby-on-rails ruby enumerable


    【解决方案1】:

    来自docs of Enumerable#grep

    grep(pattern) → array
    grep(pattern) { |obj| block } → array
    

    返回enum 中每个元素的数组,其中Pattern === element 对应。如果提供了可选块,则将每个匹配元素传递给它,并将块的结果存储在输出数组中。

    重要的部分是此方法返回计算 Pattern === elementtrue 的元素。但是/M/ === {name: 'John', grade: 8, gender: 'M'} 不会返回true,对于数组中的所有其他元素都是一样的。

    因此,您的结果集首先是空的。

    块 - 在您的示例中为 { |gend| gend[:gender] } - 仅在存在模式匹配时和之后才被评估。该块改变了整个调用的返回值,但不改变模式匹配的完成方式。

    请注意Rexgxp#=== 在此上下文中的文档。

    【讨论】:

      【解决方案2】:

      我正在创建一个过滤性别“M”并返回其元素的方法。

      考虑到上述要求,您的命名似乎颇具误导性:

      def is_male(gender)
        # ...
      end
      

      上面看起来像一个接受性别并检查是否为男性的方法。我希望是这样的:

      is_male('M') #=> true
      is_male('F') #=> false
      

      is_male(students) 也不清楚——它会检查给定数组中是否有男学生吗?或者如果所有学生都是男性?不管怎样,这听起来不像是过滤。


      让我们首先重命名方法及其参数以更符合您的要求:

      def male_students(students)
        # ....
      end
      

      如果你想使用grep,你必须提供一个pattern,它是一个响应===的对象。正则表达式不起作用,因为它对字符串进行操作,而我们的学生是哈希(更多内容见下文)。您可以改用Proc,它也响应===

      def male_students(students)
        is_male = ->(student) { student[:gender] == 'M' }
        students.grep(is_male)
      end
      

      但是使用select更方便:

      def male_students(students)
        students.select { |student| student[:gender] == 'M' }
      end
      

      另一种选择是为您的学生使用自定义课程,因为现在,您的学生只是散列:

      class Student
        attr_accessor :name, :grade, :gender
      
        def initialize(name:, grade:, gender:)
          @name = name
          @grade = grade
          @gender = gender
        end
      
        def male?
          gender == 'M'
        end
      
        def female?
          gender == 'F'
        end
      end
      

      现在,相应地更改您的数组:

      students = [
        Student.new(name: 'John', grade: 8, gender: 'M'),
        Student.new(name: 'Sarah', grade: 12, gender: 'F'),
        Student.new(name: 'Bob', grade: 16, gender: 'M'),
        Student.new(name: 'Johnny', grade: 2, gender: 'M'),
        Student.new(name: 'Ethan', grade: 4, gender: 'M'),
        Student.new(name: 'Paula', grade: 8, gender: 'F'),
        Student.new(name: 'Donald', grade: 5, gender: 'M'),
        Student.new(name: 'Jennifer', grade: 13, gender: 'F'),
        Student.new(name: 'Courtney', grade: 15, gender: 'F'),
        Student.new(name: 'Jane', grade: 9, gender: 'F'),
      ]
      

      你可以使用这个非常简单的语法:

      students.select(&:male?)
      #=>
      # [
      #   #<Student:0x00007fb18d826ce8 @name="John", @grade=8, @gender="M">,
      #   #<Student:0x00007fb18d826b58 @name="Bob", @grade=16, @gender="M">,
      #   #<Student:0x00007fb18d826a90 @name="Johnny", @grade=2, @gender="M">,
      #   #<Student:0x00007fb18d8269c8 @name="Ethan", @grade=4, @gender="M">,
      #   #<Student:0x00007fb18d826838 @name="Donald", @grade=5, @gender="M">
      #  ]
      

      Array/Enumerable中的任何其他有用方法:

      students.any?(&:male?)  #=> true
      students.all?(&:male?)  #=> false
      students.count(&:male?) #=> 5
      

      【讨论】:

        猜你喜欢
        • 2012-12-11
        • 1970-01-01
        • 2014-11-15
        • 1970-01-01
        • 2010-11-10
        • 2014-08-14
        • 2011-03-03
        • 2012-08-23
        • 2017-05-28
        相关资源
        最近更新 更多