Який найпростіший спосіб видалити перший символ із рядка?


174

Приклад:

[12,23,987,43

Який найшвидший і найефективніший спосіб видалити " [", використовуючи можливо, chop()але для першого символу?


1
Я відредагував свою відповідь, щоб можливо змінити обрану відповідь. Подивіться, чи можете ви присвоїти його відповіді Джейсона Стірка, оскільки його найшвидший і дуже читабельний.
Олов'яний чоловік

3
Використовуйте str [1 ..- 1], найшвидший за відповідними відповідями.
Achyut Rastogi

1
Станом на Ruby 2.5 ви можете використовувати delete_prefixта delete_prefix!- більш детальну інформацію нижче . У мене не було часу на тестування, але незабаром!
SRack

Оновлення: Я орієнтував нові методи ( delete_prefix\ delete_prefix!), і вони досить швидкі. Не дуже перешкоджають попереднім фаворитам за швидкістю, але читабельність означає, що у них є чудові нові варіанти!
SRack

Відповіді:


233

Мені подобається використовувати щось на кшталт:

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

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

Я завжди шукаю найшвидший і найчитабельніший спосіб вчинити:

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:

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)

Оновлення, щоб включити ще одну запропоновану відповідь:

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

Результати:

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)

І ще один, який використовує /^./для пошуку першого символу:

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

Результати:

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

Ось ще одне оновлення щодо швидшого обладнання та новішої версії Ruby:

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 так повільно?

Після пошуку / заміни gsubмає перевірити можливі додаткові збіги, перш ніж він зможе сказати, чи закінчено це. subлише один і закінчує. Подумайте, gsubяк мінімум два subдзвінки.

Крім того, важливо пам’ятати про це gsub, а subтакож може бути спричинено погано написаний регулярний вираз, який відповідає набагато повільніше, ніж пошук підрядків. По можливості закріпіть регулярний вираз, щоб отримати максимальну швидкість від нього. Тут є відповіді на "Переповнення стека", які демонструють, що тому шукайте, якщо вам потрібно більше інформації.


34
Важливо зазначити, що це буде працювати лише в Ruby 1.9. У Ruby 1.8 це видалить перший байт із рядка, а не перший символ, що не є тим, чого хоче OP.
Йорг W Міттаг

+1: Я завжди забуваю, що в позиції рядка ви можете призначити не лише один символ, але також можете вставити підрядку. Дякую!
quetzalcoatl

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

4
Це видаляє його з усіх позицій, а це не те, чого хотів ОП: "... для першого персонажа?".
Олов'яний чоловік

2
" what about "[12,23,987,43".shift ?"? Як щодо "[12,23,987,43".shift NoMethodError: undefined method зміни "на" [12,23,987,43 ": Рядок`?
Олов'яний чоловік

293

Схожа на відповідь Пабло вище, але засіб для чищення відтінків:

str[1..-1]

Поверне масив з 1 до останнього символу.

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

13
+1 Погляньте на результати еталону, які я додав до своєї відповіді. У вас найшвидший пробіг, плюс я думаю, що це дуже чисто.
Олов'яний чоловік

Що щодо продуктивності str[1,]порівняно з вищезазначеним?
Бор

1
@Bohr: str[1,]поверне вам другий символ, оскільки діапазон є 1:nil. Вам потрібно буде вказати фактичну обчислену довжину або щось гарантовано перевищувати довжину, наприклад str[1,999999](використовувати звичайно int_max), щоб отримати весь хвіст. [1..-1]чистіше і, ймовірно, швидше, оскільки вам не потрібно працювати в довжину вручну (див. [1..length] у
еталоні

4
Дуже приємне рішення. До речі, якщо ви хочете видалити першого та останнього персонажа:str[1..-2]
pisaruk

50

Для цього ми можемо використовувати фрагмент:

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

За допомогою slice!ми можемо видалити будь-який символ, вказавши його індекс.


2
Цей елегантний slice!(0)справді повинен бути обраною відповіддю, оскільки використовувати asdf[0] = '' для видалення першого персонажа смішно (подібно до використання gsub з регулярними виразками та зйомки мухи з гаубицею).
f055

1
Хоча це може здатися неінтуїтивним на поверхні, []=не потрібно стільки основного коду С, де slice!потрібна додаткова робота. Це додає. Аргумент може бути: "Що читабельніше?" Мені здається []=, що я читаю, але я виходжу з C -> Perl фону, який, ймовірно, фарбує моє мислення. Розробники Java, напевно, вважають, що це не менш читано. Будь-який прийнятний спосіб виконання завдання, якщо це легко зрозуміти та підтримувати і не завантажувати надмірно центральний процесор.
Бляшаний чоловік

Гаразд. Чи знаєте ви, як ми можемо виміряти, якщо функція займає велике навантаження процесора в ROR? чи ми повинні використовувати різницю у часі виконання в мілі або наносекундах?
balanv

18

Рубін 2,5+

З Ruby 2.5 ви можете використовувати delete_prefixчи delete_prefix!досягти цього в читаному вигляді.

У цьому випадку "[12,23,987,43".delete_prefix("[").

Більше інформації тут:

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

Зверніть увагу: ви також можете використовувати це для видалення елементів з кінця рядка за допомогою delete_suffixтаdelete_suffix!

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

Редагувати:

Використовуючи орієнтир Tin Man, він також виглядає досить швидко (під останніми двома записами delete_pта delete_p!). Це не зовсім перешкоджає попереднім подорожам за швидкістю, хоча дуже легко читається.

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

Якщо ви завжди хочете зняти провідні дужки:

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

Якщо ви просто хочете видалити перший символ, і ви знаєте, він не буде в багатобайтовому наборі символів:

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

або

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

1
Я б використав "[12,23,987,43".sub(/^\[+/, "")замість цього gsub(/^\[/, ""). Перший дозволяє двигуну регулярного вибору знаходити всі збіги, потім вони замінюються за одну дію і призводять до приблизно в 2 рази покращення швидкості з Ruby 1.9.3.
Олов'яний чоловік

1
Оскільки ми маємо справу зі струнами, це має бути gsub(/\A\[/, "") ?
Сагар Пандія


4

Наприклад: a = "Один два три"

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" 

Таким чином ви можете видалити один за одним перший символ рядка.


3
Це те саме, що відповідь Джейсона Стірка тільки його було подано за багато місяців до цього.
Олов'яний чоловік

3

Легкий шлях:

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

removed = str[1..str.length]

Дивовижний спосіб:

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

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

(Примітка: віддайте перевагу легкому шляху :))


1
Якщо ви хочете зберегти семантику "рубати", ви могли б просто"[12,23,987,43".reverse.chop.reverse
Кріс Хілд

це досить великі показники на вищому рівні, щоб зняти один шар
Пабло Фернандес

7
чому б не використовувати [1 ..- 1], а не [1..self.length]?
horseyguy

Приклад виправлення мавп досить непоганий для цього питання, це просто неактуально і негарно ІМО.
dredozubov

3

Спасибі @ the-tin-man за те, що вони зібрали орієнтири!

На жаль, мені не дуже подобається жодне з цих рішень. Або їм потрібен додатковий крок для отримання результату ( [0] = '', .strip!) або вони не дуже смислові / чіткі щодо того, що відбувається ( [1..-1]: "Гм, діапазон від 1 до мінус 1? Год?"), Або вони повільні або тривалі до виписати ( .gsub, .length).

Ми намагаємось - це "зсув" (на мові Array), але повернення решти символів, а не те, що було зміщено. Давайте скористаємося нашим Ruby, щоб зробити це можливим за допомогою струн! Ми можемо скористатись операцією швидкої дужки, але присвоїти їй гарне ім’я та взяти аргумент, щоб вказати, наскільки ми хочемо відхилити передню частину:

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

Але є багато іншого, що ми можемо зробити із цією швидкою, але непростою операцією дужки. Поки ми в цьому, для повноти напишемо a #shiftі #firstдля String (чому Array має усі забави‽‽), взявши аргумент, щоб вказати, скільки символів ми хочемо видалити спочатку:

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

Гаразд, зараз у нас є хороший чіткий спосіб витягування символів з передньої частини рядка, методом, який відповідає Array#firstі Array#shift(який насправді повинен бути методом чубчика ??). І ми можемо легко отримати і модифікований рядок #eat!. Гм, чи повинні ми поділитися нашою новою eat!владою з Array? Чому ні!

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

Тепер ми можемо:

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

Так краще!


2
Я пам'ятаю дискусію років тому на форумах Perl про таку функцію з ім'ям chip()замість chop()chimp()як аналог chomp()).
Марк Томас,

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

str[0] = ""

7
Важливо зазначити, що це буде працювати лише в Ruby 1.9. В Ruby 1.8, це видалить перші байти з рядка, а НЕ перший символу, який не те , що ОП хоче.
Йорг W Міттаг


0

Використання регулярного вираження:

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

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

0

Я вважаю приємним рішенням str.delete(str[0])його читабельності, хоча я не можу засвідчити її ефективність.


0

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

# => [2,3,4]

Список скидає один або кілька елементів із початку масиву, не мутує масив і повертає масив замість елементу, що випав.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.