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?


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.


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"

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.


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)


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)

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


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.


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


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!


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

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

0

Korzystanie z wyrażenia regularnego:

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

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

0

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


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.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.