Jaki jest najłatwiejszy sposób usunięcia pierwszego znaku z ciągu?

174

Przykład:

[12,23,987,43

Jaki jest najszybszy i najbardziej efektywny sposób na usunięcie znaku „ [”, używając może chop()ale dla pierwszego znaku?

NullVoxPopuli
źródło
1
Zmieniłem odpowiedź, więc może być możliwa zmiana wybranej odpowiedzi. Sprawdź, czy możesz przyznać to odpowiedzi Jasona Stirka, ponieważ jego jest najszybsza i bardzo czytelna.
Tin Man
3
Użyj str [1 ..- 1], najszybciej zgodnie z odpowiedziami poniżej.
Achyut Rastogi,
1
Od wersji Ruby 2.5 możesz używać delete_prefixi delete_prefix!- więcej szczegółów poniżej . Nie miałem czasu na testy porównawcze, ale wkrótce to zrobię!
SRack
Aktualizacja: przetestowałem nowe metody ( delete_prefix\ delete_prefix!) i są one dość szybkie. Nie całkiem poprawiają poprzednie ulubione pod względem szybkości, ale czytelność oznacza, że ​​są świetnymi nowymi opcjami!
SRack

Odpowiedzi:

233

Preferuję użycie czegoś takiego jak:

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

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

Zawsze szukam najszybszego i najbardziej czytelnego sposobu robienia rzeczy:

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

Działa na moim Macu Pro:

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)

Aktualizacja w celu uwzględnienia jeszcze jednej sugerowanej odpowiedzi:

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

Co skutkuje w:

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)

I jeszcze jedno użycie, /^./aby znaleźć pierwszą postać:

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

Co skutkuje w:

# >> 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)

Oto kolejna aktualizacja szybszego sprzętu i nowszej wersji Rubiego:

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)

Dlaczego gsub działa tak wolno?

Po wykonaniu wyszukiwania / zamiany gsubmusi sprawdzić możliwe dodatkowe dopasowania, zanim będzie mógł stwierdzić, czy zostało zakończone. subrobi tylko jedno i kończy. Rozważ, gsubże to minimum dwa subpołączenia.

Ważne jest również, aby o tym pamiętać gsubi submoże być również utrudnione przez źle napisane wyrażenie regularne, które dopasowuje się znacznie wolniej niż wyszukiwanie podłańcucha. Jeśli to możliwe, zakotwicz wyrażenie regularne, aby uzyskać jak największą szybkość. Tutaj znajdziesz odpowiedzi na Stack Overflow, które to potwierdzają, więc poszukaj więcej informacji.

Blaszany Człowiek
źródło
34
Należy zauważyć, że będzie to działać tylko w Rubim 1.9. W Ruby 1.8 usunie to pierwszy bajt z łańcucha, a nie pierwszy znak, co nie jest tym, czego chce OP.
Jörg W Mittag
+1: Zawsze zapominam, że do pozycji łańcucha można przypisać nie tylko pojedynczy znak, ale także wstawić podciąg. Dzięki!
quetzalcoatl
"[12,23,987,43".delete "["
rupweb
4
To usuwa go ze wszystkich pozycji, czego nie chciał OP: „… dla pierwszego znaku?”.
Tin Man,
2
what about "[12,23,987,43".shift ?”? A co z "[12,23,987,43".shift NoMethodError: undefined method shift „for„ [12,23,987,43 ”: String”?
Tin Man
293

Podobne do odpowiedzi Pablo powyżej, ale środek do czyszczenia odcieni:

str[1..-1]

Zwróci tablicę od 1 do ostatniego znaku.

'Hello World'[1..-1]
 => "ello World"
Jason Stirk
źródło
13
+1 Spójrz na wyniki testów porównawczych, które dodałem do mojej odpowiedzi. Masz najszybszy czas działania, a ponadto myślę, że jest bardzo czysty.
Tin Man
A co z wydajnością w str[1,]porównaniu z powyższymi?
Bohr
1
@Bohr: str[1,]zwraca drugi znak od zakresu 1:nil. Musisz podać rzeczywistą obliczoną długość lub coś, co gwarantuje, że będzie większa niż długość, na przykład str[1,999999](użyj oczywiście int_max), aby uzyskać cały ogon. [1..-1]jest czystszy i prawdopodobnie szybszy, ponieważ nie trzeba ręcznie operować na długości (patrz [1..length] w teście porównawczym)
quetzalcoatl
4
Bardzo fajne rozwiązanie. Nawiasem mówiąc, jeśli ktoś chce usunąć pierwsze i ostatnie znaki:str[1..-2]
pisaruk
50

Możemy do tego użyć slice:

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

Używając slice!możemy usunąć dowolny znak, podając jego indeks.

balanv
źródło
2
Ta elegancka slice!(0)naprawdę powinna być wybraną odpowiedzią, ponieważ użycie asdf[0] = '' do usunięcia pierwszego znaku jest absurdalne (tak jak używanie gsub z wyrażeniem regularnym i strzelanie w muchę z haubicy).
f055
1
Choć z pozoru może się to wydawać nieintuicyjne, []=nie wymaga tak dużej ilości kodu w języku C, gdzie slice!wymaga dodatkowej pracy. To się sumuje. Argument mógłby brzmieć „Który jest bardziej czytelny?” Uważam, że używanie jest []=czytelne, ale pochodzę z tła C -> Perl, które prawdopodobnie koloruje moje myślenie. Programiści Java prawdopodobnie pomyśleliby, że jest mniej czytelny. Albo jest to akceptowalny sposób wykonania zadania, o ile jest łatwy do zrozumienia i utrzymania oraz nie obciąża nadmiernie procesora.
Tin Man
Dobrze. Czy wiesz, jak możemy zmierzyć, jeśli funkcja zajmuje duże obciążenie procesora w ROR? czy też powinniśmy użyć różnicy czasu wykonania w mililitrach lub nanosekundach?
balanv
18

Ruby 2.5+

Od wersji Ruby 2.5 możesz używać delete_prefixlub delete_prefix!osiągać to w czytelny sposób.

W tym przypadku "[12,23,987,43".delete_prefix("[").

Więcej informacji tutaj:

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

Uwaga: możesz również użyć tego do usunięcia elementów z końca łańcucha za pomocą delete_suffixidelete_suffix!

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

Edytować:

Korzystając z konfiguracji testowej Tin Mana, wygląda to również dość szybko (pod dwoma ostatnimi wpisami delete_pi delete_p!). Nie do końca wypina poprzednie ulubione pod względem szybkości, ale jest bardzo czytelny.

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)
SRack
źródło
17

Wolę to:

str = "[12,23,987,43"
puts str[1..-1]
>> 12,23,987,43
henriquesuda
źródło
3
Przed powtórzeniem możesz sprawdzić inne odpowiedzi. Zostało to już zasugerowane przez stackoverflow.com/a/3614642/128421
The Tin Man
14

Jeśli zawsze chcesz usunąć nawiasy wiodące:

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

Jeśli chcesz tylko usunąć pierwszy znak i wiesz, że nie będzie to wielobajtowego zestawu znaków:

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

lub

"[12,23,987,43".slice(1..-1)
Chris Heald
źródło
1
Użyłbym "[12,23,987,43".sub(/^\[+/, "")zamiast gsub(/^\[/, ""). Pierwsza pozwala silnikowi regex znaleźć wszystkie dopasowania, a następnie są one zastępowane w jednej akcji i powoduje około dwukrotną poprawę szybkości w przypadku Rubiego 1.9.3.
Tin Man
1
Skoro mamy do czynienia ze sznurkami, czy tak powinno być gsub(/\A\[/, "") ?
Sagar Pandya
6

Nieefektywna alternatywa:

str.reverse.chop.reverse
jaamun
źródło
4

Na przykład: a = "Raz, dwa, trzy"

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" 

W ten sposób możesz usuwać pierwszy znak łańcucha jeden po drugim.

Rubyist
źródło
3
To jest to samo, co odpowiedź Jasona Stirka, tylko że jego została przesłana wiele miesięcy wcześniej.
Tin Man
3

Łatwy sposób:

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

removed = str[1..str.length]

Niesamowity sposób:

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

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

(Uwaga: wolę łatwiejszy sposób :))

Pablo Fernandez
źródło
1
Jeśli chcesz zachować semantykę „chop”, możesz po prostu"[12,23,987,43".reverse.chop.reverse
Chris Heald,
to całkiem spora suma wyników tylko po to, żeby rozebrać jedną postać
Pablo Fernandez
7
dlaczego nie użyć [1 ..- 1] zamiast [1..self.length]?
horseyguy
Przykład łatania małpy jest całkiem nie na to pytanie, to po prostu nieistotna i brzydka IMO.
dredozubov
3

Podziękowania dla @ the-tin-man za zestawienie wzorców!

Niestety, nie podoba mi się żadne z tych rozwiązań. Albo wymagają dodatkowego kroku, aby uzyskać wynik ( [0] = '', .strip!), albo nie są zbyt semantyczne / jasne, co się dzieje ( [1..-1]: „Hm, zakres od 1 do minus 1? Yearg?”), Albo są powolne lub długie napisz ( .gsub, .length).

To, co próbujemy, to „przesunięcie” (w żargonie tablicowym), ale zwrócenie pozostałych znaków, a nie tego, co zostało przesunięte. Użyjmy naszego Rubiego, aby było to możliwe dzięki łańcuchom! Możemy użyć operacji szybkiego nawiasu, ale nadaj jej dobrą nazwę i weź argument, aby określić, ile chcemy wydłubać z przodu:

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

Ale jest więcej, co możemy zrobić z tą szybką, ale nieporęczną operacją wspornika. Skoro już o tym mowa , dla kompletności napiszmy a #shifti #firstdla String (dlaczego Array miałoby mieć całą zabawę), biorąc argument, aby określić, ile znaków chcemy usunąć od początku:

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

Ok, teraz mamy dobry, jasny sposób na wyciąganie znaków z przodu łańcucha, przy użyciu metody zgodnej z Array#firsti Array#shift(która naprawdę powinna być metodą huk ??). I możemy łatwo uzyskać zmodyfikowany ciąg za pomocą #eat!. Hm, czy powinniśmy podzielić się naszą nową eat!mocą z Array? Dlaczego nie!

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

Teraz możemy:

> 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] 

Tak lepiej!

brookr
źródło
2
Pamiętam dyskusję lata temu na forach Perla na temat takiej funkcji o nazwie chip()zamiast chop()(i chimp()jako analog chomp()).
Mark Thomas
2
str = "[12,23,987,43"

str[0] = ""
Rzemieślnik
źródło
7
Należy zauważyć, że zadziała to tylko w Rubim 1.9. W Ruby 1.8 usunie to pierwszy bajt z łańcucha, a nie pierwszy znak, co nie jest tym, czego chce OP.
Jörg W Mittag
0
class String
  def bye_felicia()
    felicia = self.strip[0] #first char, not first space.
    self.sub(felicia, '')
  end
end
Josh Brody
źródło
0

Korzystanie z wyrażenia regularnego:

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

str[/.{#{str.size-n}}\z/] #=> "tring"
Sagar Pandya
źródło
0

Znajduję dobre rozwiązanie ze względu str.delete(str[0])na jego czytelność, choć nie mogę zaświadczyć o jego wydajności.

zeitchef
źródło
0

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

# => [2,3,4]

Lista usuwa jeden lub więcej elementów z początku tablicy, nie zmienia tablicy i zwraca samą tablicę zamiast upuszczonego elementu.

Aaron Henderson
źródło