【问题标题】:Ruby: String no longer mixes in Enumerable in 1.9Ruby:字符串在 1.9 中不再混入 Enumerable
【发布时间】:2011-01-17 00:22:11
【问题描述】:

那我怎么还能写出漂亮的代码,比如:

'im a string meing!'.pop

注意:str.chop 不是足够的答案

【问题讨论】:

  • 因为它从“test”返回“tes”。与 .pop 返回 't' 的方式不同
  • 在 1.8.7 中也不起作用——我不知道它应该做什么。我认为这本身就是一个警告信号:当 Ruby 代码不是立即显而易见时,它可能不是最佳的。
  • 请注意,在 1.8 中,字符串可以按行枚举。因此,即使 string.pop 曾经工作过(情况并非如此,因为 Enumerable 没有 pop 或任何其他变异方法),它也会删除最后一个 line,而不是你看起来的 word期待。
  • 如果我需要一个例子来说明为什么让程序员有权在脚上开枪是不好的,我明白了。谢谢!

标签: ruby code-formatting


【解决方案1】:

这不是可枚举字符串最终枚举的内容。一个字符串是一个序列...

  • 行,
  • 字符,
  • 代码点或
  • 字节?

答案是:所有这些,其中任何一个,其中一个或两个都不是,具体取决于上下文。因此,你必须告诉 Ruby 你真正想要的是哪一个。

String 类中有几个方法可以返回上述任何一个的枚举数。如果您想要 1.9 之前的行为,您的代码示例将是

'im a string meing!'.bytes.to_a.pop

这看起来有点难看,但这是有原因的:字符串是一个序列。您将其视为堆栈。堆栈不是序列,实际上它几乎是序列的相反

【讨论】:

  • 不错的答案,但该字符串的所有原始字符仍在内存中。
  • 好吧,我想你总是可以def String.pop; self.bytes.to_a.pop; end
【解决方案2】:

那不漂亮:)

另外,#pop 不是 Enumerable 的一部分,它是 Array 的一部分。

String 不可枚举的原因是没有“自然”单位可以枚举,应该以字符为单位还是以行为单位?因为这个字符串没有#each

String 改为提供 #each_char 和 #each_byte 以及 #each_line 方法以您选择的方式进行迭代。

【讨论】:

  • 我知道,我正在学习。但是比 str[str.lenght] 好很多
  • 使用 str[-1] 获取最后一个字符。
  • 我知道,但我对这台自动取款机有点固执。嗯,其实这还不错。
【解决方案3】:

既然你不喜欢str[str.length],那怎么样

'im a string meing!'[-1]  # returns last character as a character value

'im a string meing!'[-1,1]  # returns last character as a string

或者,如果您还需要对其进行适当的修改,同时保持简单的单行:

class String
  def pop
    last = self[-1,1]
    self.chop!
    last
  end
end

【讨论】:

  • pop 也会删除该元素。不确定这是否是他的要求之一。
  • 没错,它确实删除了最后一个元素,这确实使事情变得更加复杂。不过,这些对我来说仍然是值得学习的好选择。
  • 更新为删除最后一个字符(在 String::pop 定义中)
【解决方案4】:
#!/usr/bin/ruby1.8

s = "I'm a string meing!"
s, last_char = s.rpartition(/./)
p [s, last_char]    # => ["I'm a string meing", "!"]

String.rpartition 是 1.9 的新内容,但已向后移植到 1.8.7。它在字符串中搜索正则表达式,从末尾开始并向后工作。它返回匹配前的字符串部分、匹配后的部分和匹配后的字符串部分(我们在这里丢弃)。

【讨论】:

    【解决方案5】:

    String#slice!String#insert 将让您更接近您想要的,而无需将您的字符串转换为数组。

    例如,要模拟Array#pop,你可以这样做:

    text = '¡Exclamation!'
    mark = text.slice! -1
    
    mark == '!'          #=> true
    text                 #=> "¡Exclamation"
    

    同样,对于Array#shift

    text = "¡Exclamation!"
    inverted_mark = text.slice! 0
    
    inverted_mark == '¡' #=> true
    text                 #=> "Exclamation!"
    

    当然,要执行Array#push,您只需使用其中一种连接方法:

    text = 'Hello'
    text << '!'          #=> "Hello!"
    text.concat '!'      #=> "Hello!!"
    

    要模拟Array#unshift,请改用String#insert,这很像切片的倒数:

    text = 'World!'
    text.insert 0, 'Hello, ' #=> "Hello, World!"
    

    您还可以使用 slice 以多种方式从字符串中间抓取块。

    首先你可以传递一个起始位置和长度:

    text = 'Something!'
    thing = text.slice 4, 5
    

    您还可以传递 Range 对象来获取绝对位置:

    text = 'This is only a test.'
    only = text.slice (8..11)
    

    在 Ruby 1.9 中,像这样使用 String#sliceString#[] 相同,但如果您使用 bang 方法 String#slice! 它实际上会删除您指定的子字符串。

    text = 'This is only a test.'
    only = text.slice! (8..12)
    text == 'This is a test.'      #=> true
    

    这里有一个稍微复杂一点的例子,我们重新实现了一个简单版本的String#gsub! 来进行搜索和替换:

    text = 'This is only a test.'
    search = 'only'
    replace = 'not'
    
    index = text =~ /#{search}/
    text.slice! index, search.length
    text.insert index, replace
    
    text == 'This is not a test.'  #=> true
    

    当然,在 99.999% 的情况下,您会想要使用前面提到的 String.gsub!,它会做同样的事情:

    text = 'This is only a test.'
    text.gsub! 'only', 'not' 
    
    text == 'This is not a test.'  #=> true
    

    参考:

    【讨论】:

      猜你喜欢
      • 2010-12-29
      • 2012-07-07
      • 2011-06-22
      • 1970-01-01
      • 2011-01-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多