Bir dizeden ilk karakteri silmenin en kolay yolu nedir?


174

Misal:

[12,23,987,43

" [" chop()Karakterini kaldırmanın en hızlı, en etkili yolu hangisi olabilir ancak ilk karakter için bir ama?


1
Cevabımı düzenledim, bu nedenle seçtiğiniz cevabı değiştirmek mümkün olabilir. Jason Stirk'in cevabına en hızlı ve çok okunabilir olduğu için bunu ödeyip ödeyemeyeceğinize bakın.
Kalay Adam

3
Aşağıdaki cevaplara göre en hızlı olan str [1 ..- 1] 'i kullanın.
Achyut Rastogi

1
Ruby 2.5'den itibaren delete_prefixve delete_prefix!- daha fazla ayrıntıyı aşağıda bulabilirsiniz . Kıyaslama için zamanım olmadı, ama yakında yapacağım!
17'de SRack

Güncelleme: Yeni yöntemleri karşılaştırdım ( delete_prefix\ delete_prefix!) ve oldukça hızlılar. Hız için önceki favorileri tam olarak kullanmayın, ancak okunabilirlik, sahip olmaları için harika yeni seçenekler oldukları anlamına gelir!
SRack

Yanıtlar:


233

Böyle bir şey kullanarak iyilik:

asdf = "[12,23,987,43"
asdf [0] = '' 

p asdf
# >> "12,23,987,43"

Her zaman bir şeyler yapmanın en hızlı ve en okunabilir yolunu arıyorum:

require 'benchmark'

N = 1_000_000

puts RUBY_VERSION

STR = "[12,23,987,43"

Benchmark.bm(7) do |b|
  b.report('[0]') { N.times { "[12,23,987,43"[0] = '' } }
  b.report('sub') { N.times { "[12,23,987,43".sub(/^\[+/, "") } }

  b.report('gsub') { N.times { "[12,23,987,43".gsub(/^\[/, "") } }
  b.report('[1..-1]') { N.times { "[12,23,987,43"[1..-1] } }
  b.report('slice') { N.times { "[12,23,987,43".slice!(0) } }
  b.report('length') { N.times { "[12,23,987,43"[1..STR.length] } }

end

Mac Pro'mda çalışıyor:

1.9.3
              user     system      total        real
[0]       0.840000   0.000000   0.840000 (  0.847496)
sub       1.960000   0.010000   1.970000 (  1.962767)
gsub      4.350000   0.020000   4.370000 (  4.372801)
[1..-1]   0.710000   0.000000   0.710000 (  0.713366)
slice     1.020000   0.000000   1.020000 (  1.020336)
length    1.160000   0.000000   1.160000 (  1.157882)

Önerilen bir yanıtı daha içerecek şekilde güncelleniyor:

require 'benchmark'

N = 1_000_000

class String
  def eat!(how_many = 1)
    self.replace self[how_many..-1]
  end

  def first(how_many = 1)
    self[0...how_many]
  end

  def shift(how_many = 1)
    shifted = first(how_many)
    self.replace self[how_many..-1]
    shifted
  end
  alias_method :shift!, :shift
end

class Array
  def eat!(how_many = 1)
    self.replace self[how_many..-1]
  end
end

puts RUBY_VERSION

STR = "[12,23,987,43"

Benchmark.bm(7) do |b|
  b.report('[0]') { N.times { "[12,23,987,43"[0] = '' } }
  b.report('sub') { N.times { "[12,23,987,43".sub(/^\[+/, "") } }

  b.report('gsub') { N.times { "[12,23,987,43".gsub(/^\[/, "") } }
  b.report('[1..-1]') { N.times { "[12,23,987,43"[1..-1] } }
  b.report('slice') { N.times { "[12,23,987,43".slice!(0) } }
  b.report('length') { N.times { "[12,23,987,43"[1..STR.length] } }
  b.report('eat!') { N.times { "[12,23,987,43".eat! } }
  b.report('reverse') { N.times { "[12,23,987,43".reverse.chop.reverse } }
end

Sonuç:

2.1.2
              user     system      total        real
[0]       0.300000   0.000000   0.300000 (  0.295054)
sub       0.630000   0.000000   0.630000 (  0.631870)
gsub      2.090000   0.000000   2.090000 (  2.094368)
[1..-1]   0.230000   0.010000   0.240000 (  0.232846)
slice     0.320000   0.000000   0.320000 (  0.320714)
length    0.340000   0.000000   0.340000 (  0.341918)
eat!      0.460000   0.000000   0.460000 (  0.452724)
reverse   0.400000   0.000000   0.400000 (  0.399465)

Ve bir başkası /^./ilk karakteri bulmak için kullanıyor :

require 'benchmark'

N = 1_000_000

class String
  def eat!(how_many = 1)
    self.replace self[how_many..-1]
  end

  def first(how_many = 1)
    self[0...how_many]
  end

  def shift(how_many = 1)
    shifted = first(how_many)
    self.replace self[how_many..-1]
    shifted
  end
  alias_method :shift!, :shift
end

class Array
  def eat!(how_many = 1)
    self.replace self[how_many..-1]
  end
end

puts RUBY_VERSION

STR = "[12,23,987,43"

Benchmark.bm(7) do |b|
  b.report('[0]') { N.times { "[12,23,987,43"[0] = '' } }
  b.report('[/^./]') { N.times { "[12,23,987,43"[/^./] = '' } }
  b.report('[/^\[/]') { N.times { "[12,23,987,43"[/^\[/] = '' } }
  b.report('sub+') { N.times { "[12,23,987,43".sub(/^\[+/, "") } }
  b.report('sub') { N.times { "[12,23,987,43".sub(/^\[/, "") } }
  b.report('gsub') { N.times { "[12,23,987,43".gsub(/^\[/, "") } }
  b.report('[1..-1]') { N.times { "[12,23,987,43"[1..-1] } }
  b.report('slice') { N.times { "[12,23,987,43".slice!(0) } }
  b.report('length') { N.times { "[12,23,987,43"[1..STR.length] } }
  b.report('eat!') { N.times { "[12,23,987,43".eat! } }
  b.report('reverse') { N.times { "[12,23,987,43".reverse.chop.reverse } }
end

Sonuç:

# >> 2.1.5
# >>               user     system      total        real
# >> [0]       0.270000   0.000000   0.270000 (  0.270165)
# >> [/^./]    0.430000   0.000000   0.430000 (  0.432417)
# >> [/^\[/]   0.460000   0.000000   0.460000 (  0.458221)
# >> sub+      0.590000   0.000000   0.590000 (  0.590284)
# >> sub       0.590000   0.000000   0.590000 (  0.596366)
# >> gsub      1.880000   0.010000   1.890000 (  1.885892)
# >> [1..-1]   0.230000   0.000000   0.230000 (  0.223045)
# >> slice     0.300000   0.000000   0.300000 (  0.299175)
# >> length    0.320000   0.000000   0.320000 (  0.325841)
# >> eat!      0.410000   0.000000   0.410000 (  0.409306)
# >> reverse   0.390000   0.000000   0.390000 (  0.393044)

Daha hızlı donanım ve Ruby'nin daha yeni bir sürümü hakkında başka bir güncelleme:

2.3.1
              user     system      total        real
[0]       0.200000   0.000000   0.200000 (  0.204307)
[/^./]    0.390000   0.000000   0.390000 (  0.387527)
[/^\[/]   0.360000   0.000000   0.360000 (  0.360400)
sub+      0.490000   0.000000   0.490000 (  0.492083)
sub       0.480000   0.000000   0.480000 (  0.487862)
gsub      1.990000   0.000000   1.990000 (  1.988716)
[1..-1]   0.180000   0.000000   0.180000 (  0.181673)
slice     0.260000   0.000000   0.260000 (  0.266371)
length    0.270000   0.000000   0.270000 (  0.267651)
eat!      0.400000   0.010000   0.410000 (  0.398093)
reverse   0.340000   0.000000   0.340000 (  0.344077)

GSUB neden bu kadar yavaş?

Bir arama / değiştirme yaptıktan sonra, gsubtamamlanıp tamamlanmadığını söyleyebilmek için olası ek eşleşmeleri kontrol etmelidir. subsadece birini yapar ve bitirir. En gsubaz iki subçağrı gibi düşünün .

Ayrıca, bunu hatırlamak önemlidir gsubve subayrıca bir alt dize aramasından çok daha yavaş eşleşen kötü yazılmış regex ile de özürlü olabilir. Mümkünse en yüksek hızı elde etmek için normal ifadeyi sabitleyin. Stack Overflow'da, daha fazla bilgi istiyorsanız etrafta arama yaptığınızı gösteren cevaplar vardır.


34
Bunun sadece Ruby 1.9'da işe yarayacağını belirtmek önemlidir. Ruby 1.8'de bu , OP'nin istediği ilk karakter değil , ilk baytı dizeden kaldıracaktır .
Jörg W Mittag

+1: Her zaman bir dize konumuna yalnızca tek bir karakter atamakla kalmayıp bir alt dize de ekleyebileceğinizi unuturum. Teşekkürler!
quetzalcoatl

"[12,23,987,43".delete "["
rupweb

4
Bu onu tüm pozisyonlardan siler, OP'nin istediği bu değildir: "... ilk karakter için?".
Tin Man

2
" what about "[12,23,987,43".shift ?"? "[12,23,987,43".shift NoMethodError: undefined method "[12,23,987,43": shift` için shift ne olacak?
Tin Man

293

Pablo'nun yukarıdaki cevabına benzer, ancak bir gölge temizleyici:

str[1..-1]

Diziyi 1'den son karaktere döndürür.

'Hello World'[1..-1]
 => "ello World"

13
+1 Cevabıma eklediğim karşılaştırma sonuçlarına bir göz atın. En hızlı çalışma süresine sahipsiniz, ayrıca bence çok temiz.
Kalay Adam

str[1,]Yukarıdakilere kıyasla performansı ne olacak ?
Bohr

1
@Bohr: str[1,]aralık olduğu için size ikinci karakteri döndürür 1:nil. str[1,999999]Tüm kuyruğu almak için gerçek hesaplanan uzunluğu veya uzunluktan daha yüksek olduğu garanti edilen bir şey sağlamanız gerekir ( örneğin, int_max'ı kullanın). [1..-1]daha uzun ve muhtemelen daha hızlıdır, çünkü uzunluğu manuel olarak çalıştırmanıza gerek yoktur (karşılaştırmada [1. uzunluk] 'a bakın)
quetzalcoatl

4
Çok güzel bir çözüm. Bu arada, ilk ve son karakterleri kaldırmak istiyorsa:str[1..-2]
pisaruk

50

Bunu yapmak için dilimi kullanabiliriz:

val = "abc"
 => "abc" 
val.slice!(0)
 => "a" 
val
 => "bc" 

Kullanarak slice!herhangi bir karakteri dizini belirterek silebiliriz.


2
Bu zarif slice!(0)gerçekten seçilen cevap olmalı, çünkü asdf[0] = '' ilk karakteri kaldırmak için gülünç (tıpkı regex ile gsub kullanmak ve bir obüs ile bir sinek atmak gibi).
f055

1
Yüzeyde sezgisel görünmese de, ek iş gerektiren []=altta yatan C kodu slice!gerektirmez. Bu da artıyor. Argüman "Hangisi daha okunabilir?" []=Okunabilir kullanarak buluyorum , ama muhtemelen düşüncemi renklendiren bir C -> Perl arka planından geliyorum. Java geliştiricileri muhtemelen daha az okunabilir olduğunu düşünürler. Her ikisi de, kolayca anlaşıldığı ve bakımı yapılabildiği ve CPU'yu gereksiz yere yüklemediği sürece görevi gerçekleştirmenin kabul edilebilir bir yoludur.
Tin Man

Tamam. Bir fonksiyonun ROR'da çok fazla CPU yükü alıp almadığını nasıl ölçebileceğimizi biliyor musunuz? veya yürütme süresi farkını mili veya nanosaniye olarak mı kullanmalıyız?
balanv

18

Yakut 2.5+

Ruby 2.5'ten itibaren bunu okunabilir bir şekilde elde etmek için delete_prefixveya kullanabilirsiniz delete_prefix!.

Bu durumda "[12,23,987,43".delete_prefix("[").

Daha fazla bilgi burada:

'invisible'.delete_prefix('in') #=> "visible"
'pink'.delete_prefix('in') #=> "pink"

NB ayrıca bir dize sonuna öğeleri kaldırmak için kullanabilir delete_suffixvedelete_suffix!

'worked'.delete_suffix('ed') #=> "work"
'medical'.delete_suffix('ed') #=> "medical"

Düzenle:

Tin Man'ın kıyaslama kurulumunu kullanarak, oldukça hızlı görünüyor (son iki girişin altında delete_pve delete_p!). Yapmaz oldukça olsa, hız için önceki faves pip çok okunabilir.

2.5.0
              user     system      total        real
[0]       0.174766   0.000489   0.175255 (  0.180207)
[/^./]    0.318038   0.000510   0.318548 (  0.323679)
[/^\[/]   0.372645   0.001134   0.373779 (  0.379029)
sub+      0.460295   0.001510   0.461805 (  0.467279)
sub       0.498351   0.001534   0.499885 (  0.505729)
gsub      1.669837   0.005141   1.674978 (  1.682853)
[1..-1]   0.199840   0.000976   0.200816 (  0.205889)
slice     0.279661   0.000859   0.280520 (  0.285661)
length    0.268362   0.000310   0.268672 (  0.273829)
eat!      0.341715   0.000524   0.342239 (  0.347097)
reverse   0.335301   0.000588   0.335889 (  0.340965)
delete_p  0.222297   0.000832   0.223129 (  0.228455)
delete_p!  0.225798   0.000747   0.226545 (  0.231745)


14

Her zaman önde gelen braketleri soymak istiyorsanız:

"[12,23,987,43".gsub(/^\[/, "")

Sadece ilk karakteri kaldırmak istiyorsanız ve bunun çok baytlı bir karakter kümesinde olmayacağını biliyorsanız:

"[12,23,987,43"[1..-1]

veya

"[12,23,987,43".slice(1..-1)

1
Bunun "[12,23,987,43".sub(/^\[+/, "")yerine kullanırdım gsub(/^\[/, ""). Birincisi, regex motorunun tüm eşleşmeleri bulmasına izin verir, daha sonra tek bir eylemle değiştirilir ve Ruby 1.9.3 ile hızda yaklaşık 2 kat iyileşme sağlar.
Kalay Adam

1
Dizelerle uğraştığımız için bu olmalı gsub(/\A\[/, "") mı?
Sagar Pandya


4

Örneğin: a = "Bir İki Üç"

1.9.2-p290 > a = "One Two Three"
 => "One Two Three" 

1.9.2-p290 > a = a[1..-1]
 => "ne Two Three" 

1.9.2-p290 > a = a[1..-1]
 => "e Two Three" 

1.9.2-p290 > a = a[1..-1]
 => " Two Three" 

1.9.2-p290 > a = a[1..-1]
 => "Two Three" 

1.9.2-p290 > a = a[1..-1]
 => "wo Three" 

Bu şekilde dizenin ilk karakterini kaldırabilirsiniz.


3
Bu Jason Stirk'in cevabıyla aynı, ancak onun aylar önce gönderildi.
Kalay Adam

3

Kolay yol:

str = "[12,23,987,43"

removed = str[1..str.length]

Harika bir yol:

class String
  def reverse_chop()
    self[1..self.length]
  end
end

"[12,23,987,43".reverse_chop()

(Not: kolay yolu tercih edin :))


1
Eğer "doğrayın" semantiği korumak istiyorsanız sadece yapabilirsiniz"[12,23,987,43".reverse.chop.reverse
Chris Heald

Bu sadece bir char şerit için oldukça büyük bir performans yükü
Pablo Fernandez

7
neden [1..self.length] yerine [1 ..- 1] kullanmıyorsunuz?
horseyguy

Maymun yama örneği bu soru için oldukça kapalı, sadece alakasız ve çirkin IMO.
dredozubov

3

Kriterlere katıldıkları için @ the-tin-man'a teşekkürler!

Ne yazık ki, bu çözümlerin hiçbirini gerçekten sevmiyorum. Ya sonucu elde etmek için fazladan bir aşamaya ihtiyaç duyarlar ( [0] = '', .strip!) ya da olanlar hakkında çok semantik / net değiller [1..-1]: ( "Um, 1'den negatif 1? Yearg?" Aralığında) veya yavaş yazmak ( .gsub, .length).

Yapmaya çalıştığımız şey 'Dizi' (Array parlance), ancak kaydırılandan ziyade kalan karakterleri döndürmektir. Ruby ile bunu dizelerle mümkün kılmak için kullanalım! Hızlı braket işlemini kullanabiliriz, ancak buna iyi bir isim verebiliriz ve önden ne kadar kesmek istediğimizi belirtmek için bir argüman alabiliriz:

class String
  def eat!(how_many = 1)
    self.replace self[how_many..-1]
  end
end

Ancak bu hızlı ama hantal braket operasyonuyla yapabileceğimiz daha çok şey var. Biz varken , tamlık için, en baştan kaldırmak istediğimiz karakterleri belirlemek için bir argüman alarak, bir #shiftve #firstString için (neden Array'ın tüm eğlenceye sahip olması gerekir?) Yazalım:

class String
  def first(how_many = 1)
    self[0...how_many]
  end

  def shift(how_many = 1)
    shifted = first(how_many)
    self.replace self[how_many..-1]
    shifted
  end
  alias_method :shift!, :shift
end

Tamam, şimdi karakterleri bir dizenin önünden çekerek Array#firstve tutarlı bir yöntemle Array#shift(gerçekten bir patlama yöntemi olmalı ??) iyi bir açık yolumuz var . Ve kolayca değiştirilmiş dize ile alabilirsiniz #eat!. Hm, yeni eat!ing gücümüzü Array ile paylaşmalı mıyız ? Neden olmasın!

class Array
  def eat!(how_many = 1)
    self.replace self[how_many..-1]
  end
end

Şimdi yapabiliriz:

> str = "[12,23,987,43" #=> "[12,23,987,43"
> str.eat!              #=> "12,23,987,43"
> str                   #=> "12,23,987,43"

> str.eat!(3)           #=> "23,987,43"
> str                   #=> "23,987,43"

> str.first(2)          #=> "23"
> str                   #=> "23,987,43"

> str.shift!(3)         #=> "23,"
> str                   #=> "987,43"

> arr = [1,2,3,4,5]     #=> [1, 2, 3, 4, 5] 
> arr.eat!              #=> [2, 3, 4, 5] 
> arr                   #=> [2, 3, 4, 5] 

Bu daha iyi!


2
Yıllar önce Perl forumlarında chip()bunun yerine chop()(ve chimp()analogu olarak chomp()) adıyla böyle bir işlev hakkında bir tartışma hatırlıyorum .
Mark Thomas

2
str = "[12,23,987,43"

str[0] = ""

7
Bunun sadece Ruby 1.9'da işe yarayacağını belirtmek önemlidir. Ruby 1.8'de bu , OP'nin istediği ilk karakter değil , ilk baytı dizeden kaldıracaktır .
Jörg W Mittag

0
class String
  def bye_felicia()
    felicia = self.strip[0] #first char, not first space.
    self.sub(felicia, '')
  end
end

0

Normal ifade kullanarak:

str = 'string'
n = 1  #to remove first n characters

str[/.{#{str.size-n}}\z/] #=> "tring"

0

str.delete(str[0])Okunabilirliği için güzel bir çözüm buluyorum , ancak performansını kanıtlayamıyorum.


0

list = [1,2,3,4] list.drop (1)

# => [2,3,4]

Liste, dizinin başlangıcından itibaren bir veya daha fazla öğeyi bırakır, diziyi değiştirmez ve bırakılan öğe yerine dizinin kendisini döndürür.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.