Скрытые возможности Ruby


160

Продолжая тему «Скрытые возможности ...», давайте расскажем о менее известных, но полезных функциях языка программирования Ruby.

Попытайтесь ограничить это обсуждение ядром Ruby, без каких-либо вещей Ruby on Rails.

Смотрите также:

(Пожалуйста, только одна скрытая функция в ответ.)

Спасибо


должно быть сообщество вики
SilentGhost

Ответы:


80

Начиная с Ruby 1.9, Proc # === является псевдонимом вызова Proc #, что означает, что объекты Proc могут использоваться в выражениях case следующим образом:

def multiple_of(factor)
  Proc.new{|product| product.modulo(factor).zero?}
end

case number
  when multiple_of(3)
    puts "Multiple of 3"
  when multiple_of(7)
    puts "Multiple of 7"
end

1
Я на самом деле написал для этого гем, но мой код был (а) беспорядочным и (б) медленным. Я очень рад, что функциональность превратила его в ядро.
Джеймс А. Розен

76

У Питера Купера есть хороший список рубиновых трюков. Возможно, мой фаворит - разрешить перечисление как отдельных предметов, так и коллекций. (То есть обрабатывайте объект, не являющийся коллекцией, как коллекцию, содержащую только этот объект.) Это выглядит так:

[*items].each do |item|
  # ...
end

38
Более явной (и, следовательно, более приятной) формой этого является Array (items) .each
mislav

Если itemsэто строка, вам не нужно заключать ее в [*…]. String.each не выполняет итерации по символам, как некоторые могут ожидать. Он просто возвращает себя в блок.
mxcl

Какая польза от этого? Просто любопытно.
Эд С.

1
@Ed: хорошо, если вы пишете метод и хотите, чтобы пользователь метода пропустил список varargs или массив.
Джеймс А. Розен

64

Не знаю, насколько это скрыто, но я нашел это полезным, когда нужно сделать хеш из одномерного массива:

fruit = ["apple","red","banana","yellow"]
=> ["apple", "red", "banana", "yellow"]

Hash[*fruit]    
=> {"apple"=>"red", "banana"=>"yellow"}

Обратите внимание, что Hash[ [["apple","red"], ["banana","yellow"] ]дает тот же результат.
Марк-Андре Лафортун

54

Один трюк, который мне нравится, - это использовать *расширитель splat ( ) для объектов, отличных от массивов. Вот пример соответствия регулярному выражению:

match, text, number = *"Something 981".match(/([A-z]*) ([0-9]*)/)

Другие примеры включают в себя:

a, b, c = *('A'..'Z')

Job = Struct.new(:name, :occupation)
tom = Job.new("Tom", "Developer")
name, occupation = *tom

13
Между прочим, для любопытных это работает, неявно вызывая to_a для цели splat.
Боб Аман

1
Если вы не заинтересованы в матче, вы можете иметь text, number = *"text 555".match(/regexp/)[1..-1].
Эндрю Гримм

text, number = "Something 981".scan(/([A-z]*) ([0-9]*)/).flatten.map{|m| Integer(m) rescue m}
Йонас Эльфстрем

7
Оба хороших трюка, но должен быть момент, когда в нем слишком много магии, верно ?!
Томафро

1
@ Андрей, ты считал, что матч может вернуть ноль? у ноля нет метода []
Алексей

52

Вау, никто не упомянул оператор триггера:

1.upto(100) do |i|
  puts i if (i == 3)..(i == 15)
end

11
Правильно ... кому-то придется объяснить это мне. Это работает, но я не могу понять, почему.
Боб Аман

12
Оператор триггера является Statefull, если. Его состояние переключается на true, как только, i == 3и переключается на false после i != 3 и i == 15. Похоже на триггер: en.wikipedia.org/wiki/Flip-flop_%28electronics%29
Константин Хаазе

1
Я бы точно не назвал это скрытой функцией, так много раздражения. Я помню, как впервые познакомился с ним в #Ruby на Freenode много лет назад; В какой-то момент я использовал практически все функции Ruby, кроме этой.
ELLIOTTCABLE

1
Я бы не назвал это раздражением, это просто то, что вы не использовали. Я использую его, и он может хорошо сократить код, особенно когда я беру блоки строк из файлов на основе некоторых критериев.
Жестянщик

49

Одна из замечательных особенностей ruby ​​заключается в том, что вы можете вызывать методы и выполнять код в тех местах, где другие языки будут недовольны, например, в определениях методов или классов.

Например, чтобы создать класс, который имеет неизвестный суперкласс до времени выполнения, т.е. является случайным, вы можете сделать следующее:

class RandomSubclass < [Array, Hash, String, Fixnum, Float, TrueClass].sample

end

RandomSubclass.superclass # could output one of 6 different classes.

Здесь используется Array#sampleметод 1.9 (только в 1.8.7, см. Array#choice), И пример довольно надуманный, но здесь вы можете увидеть всю мощь.

Еще один крутой пример - возможность выставлять значения параметров по умолчанию, которые не являются фиксированными (как часто требуют другие языки):

def do_something_at(something, at = Time.now)
   # ...
end

Конечно, проблема с первым примером состоит в том, что он оценивается во время определения, а не во время вызова. Таким образом, после выбора суперкласса он остается этим суперклассом до конца программы.

Однако, во втором примере, каждый раз при вызове do_something_at, то atпеременная будет время , что метод был назван (ну, очень и очень близко к нему)


2
Примечание: Массив # rand предоставляется ActiveSupport, который вы можете использовать вне Rails так же легко, какrequire 'activesupport'
rfunduk

Выбор массива # в 1.8.7
Джош Ли

24
Выбор массива № 1.8.7 только ! Не используйте его, он вышел в 1.9 и будет в 1.8.8. Используйте #sample
Марк-Андре Лафортун

python: класс DictList ([dict, list] [random.randint (0,1)]): pass
Anurag Uniyal

def do_something_at (что-то, at = lambda {Time.now}) at.call #now динамически назначать время окончания
Джек Кинселла,

47

Еще одна крошечная функция - конвертировать Fixnumв любую базу до 36:

>> 1234567890.to_s(2)
=> "1001001100101100000001011010010"

>> 1234567890.to_s(8)
=> "11145401322"

>> 1234567890.to_s(16)
=> "499602d2"

>> 1234567890.to_s(24)
=> "6b1230i"

>> 1234567890.to_s(36)
=> "kf12oi"

И, как прокомментировал Хью Уолтерс, преобразование в другую сторону так же просто:

>> "kf12oi".to_i(36)
=> 1234567890

1
И для полноты, String#to_s(base)может быть использован для преобразования обратно в целое число; "1001001100101100000001011010010".to_i(2)и "499602d2".to_i(16)т. д. все возвращают оригинал Fixnum.
Хью Уолтерс

40

Хеши со значениями по умолчанию! Массив в этом случае.

parties = Hash.new {|hash, key| hash[key] = [] }
parties["Summer party"]
# => []

parties["Summer party"] << "Joe"
parties["Other party"] << "Jane"

Очень полезно в метапрограммировании.


1
Да, верно. Рубиновый хеш может принять оператор «<<», если уже есть значение по умолчанию, назначенное с помощью «=» (не важно, пустое ли это назначение), в противном случае хэш не примет «<<». CMIIW
MHD

39

Загрузите исходный код Ruby 1.9 и выполните выдачу make golf, тогда вы можете делать такие вещи:

make golf

./goruby -e 'h'
# => Hello, world!

./goruby -e 'p St'
# => StandardError

./goruby -e 'p 1.tf'
# => 1.0

./goruby19 -e 'p Fil.exp(".")'
"/home/manveru/pkgbuilds/ruby-svn/src/trunk"

Прочитайте golf_prelude.cдля более аккуратных вещей, скрывающихся.


38

Еще одно забавное дополнение в функциональности 1.9 Proc - это Proc # curry, который позволяет превратить Proc, принимающий n аргументов, в один, принимающий n-1. Здесь он объединен с советом Proc # ===, о котором я упоминал выше:

it_is_day_of_week = lambda{ |day_of_week, date| date.wday == day_of_week }
it_is_saturday = it_is_day_of_week.curry[6]
it_is_sunday = it_is_day_of_week.curry[0]

case Time.now
when it_is_saturday
  puts "Saturday!"
when it_is_sunday
  puts "Sunday!"
else
  puts "Not the weekend"
end

35

Булевы операторы на не булевых значениях.

&& и ||

Оба возвращают значение последнего оцененного выражения.

Вот почему ||= переменная обновит значение с выражением, возвращаемым справа, если переменная не определена. Это явно не задокументировано, но общеизвестно.

Однако об &&=этом не так широко известно.

string &&= string + "suffix"

эквивалентно

if string
  string = string + "suffix"
end

Это очень удобно для деструктивных операций, которые не должны продолжаться, если переменная не определена.


2
Точнее, string &&= string + "suffix" эквивалентно string = string && string + "suffix". Это &&и ||возвращение их второй аргумент обсуждается в PickAx, с. 154 (часть I - аспекты Ruby, выражения, условное выполнение).
Ричард Майкл

29

Функция Symbol # to_proc, которую предоставляет Rails, действительно классная.

Вместо того

Employee.collect { |emp| emp.name }

Ты можешь написать:

Employee.collect(&:name)

Это, по-видимому, «на порядок медленнее», чем использование блока. igvita.com/2008/07/08/6-optimization-tips-for-ruby-mri
Чарльз Ропер,

Я только что попробовал, и обнаружил, что между ними нет существенной разницы. Я не уверен, откуда взялись эти "порядки". (Использование Ruby 1.8.7)
Мэтт Гранде,

1
Делать это вне Rails также удобно и может быть сделано, require 'activesupport'потому что именно отсюда большинство этих помощников.
rfunduk

8
Раньше это было медленно из-за реализации active_support, то есть он принимал несколько аргументов, так что вы могли делать крутое дерьмо, как (1..10) .inject &: *, но основным вариантом использования часто был только вызов метода для каждого члена коллекция, например,% w (быстрая коричневая лиса) .map &: upcase. по состоянию на 1.8.7 это ядро ​​рубина и производительность приемлемая.
Стив Грэм

4
@thenduks: И это можно сделать без помощи activesupport в ruby ​​1.8.7 и 1.9.
Эндрю Гримм

28

И последнее: в ruby ​​вы можете использовать любой символ, который хотите разделить. Возьмите следующий код:

message = "My message"
contrived_example = "<div id=\"contrived\">#{message}</div>"

Если вы не хотите избегать двойных кавычек в строке, вы можете просто использовать другой разделитель:

contrived_example = %{<div id="contrived-example">#{message}</div>}
contrived_example = %[<div id="contrived-example">#{message}</div>]

Помимо того, что вам не нужно избегать разделителей, вы можете использовать эти разделители для более хороших многострочных строк:

sql = %{
    SELECT strings 
    FROM complicated_table
    WHERE complicated_condition = '1'
}

19
не какой-нибудь персонаж, но все равно довольно круто. Он также работает с другими литералами:% () /% {} /% [] /% <> /% || % r () /% r {} /% r [] /% r <> /% r || % w () /% w {} /% w [] /% w <> /% w ||
Бо Жанес

Существует также синтаксис документа herenow doc: << BLOCK ... BLOCK, который мне нравится использовать для таких вещей, как многострочные операторы SQL и т. Д.
Martin T.

26

Я считаю, что использование команды define_method для динамической генерации методов довольно интересно и не так хорошо известно. Например:

((0..9).each do |n|
    define_method "press_#{n}" do
      @number = @number.to_i * 10 + n
    end
  end

Приведенный выше код использует команду define_method для динамического создания методов «press1» - «press9». Вместо того, чтобы вводить все 10 методов, которые по сути содержат один и тот же код, команда define method используется для генерации этих методов на лету по мере необходимости.


4
Единственная проблема с define_method заключается в том, что он не позволяет передавать блоки в качестве параметров в ruby ​​1.8. Посмотрите этот пост в блоге для обходного пути.
Эндрю Гримм

26

Используйте объект Range в качестве бесконечного ленивого списка:

Inf = 1.0 / 0

(1..Inf).take(5) #=> [1, 2, 3, 4, 5]

Более подробная информация здесь: http://banisterfiend.wordpress.com/2009/10/02/wtf-infinite-ranges-in-ruby/


Lazy_select в связанной статье очень аккуратный.
Джозеф Вайсман

Это действительно круто. Мне нравится, что Infinity - это число с плавающей точкой, поэтому, когда я попробовал это: (-Inf..Inf) .take (4), он поднял (логически непротиворечиво) не может повторить ошибку float. : D
zachaysan

23

module_function

Методы модуля, которые объявлены как module_function , создадут свои копии как частные методы экземпляра в классе, который включает в себя модуль:

module M
  def not!
    'not!'
  end
  module_function :not!
end

class C
  include M

  def fun
    not!
  end
end

M.not!     # => 'not!
C.new.fun  # => 'not!'
C.new.not! # => NoMethodError: private method `not!' called for #<C:0x1261a00>

Если вы используете module_function без каких-либо аргументов, то любые методы модуля, которые идут после оператора module_function, автоматически сами становятся module_functions.

module M
  module_function

  def not!
    'not!'
  end

  def yea!
    'yea!'
  end
end


class C
  include M

  def fun
    not! + ' ' + yea!
  end
end
M.not!     # => 'not!'
M.yea!     # => 'yea!'
C.new.fun  # => 'not! yea!'

4
Если вы просто хотите объявить приватные методы в модулях, просто используйте ключевое слово private. В дополнение к закрытому методу метода в классах, которые включают модуль, module_function копирует метод в экземпляр модуля. В большинстве случаев это не то, что вы хотите.
Tomafro

Я знаю, что вы можете просто использовать личное. Но это вопрос о скрытых возможностях Руби. И я думаю, что большинство людей никогда не слышали о module_function (включая меня), пока не увидят его в документе и не начнут с ним играть.
Newtonapple

Альтернатива использованию module_function(2-й способ) - просто использовать extend self(что выглядит довольно неплохо: D)
J -_- L


21

Предупреждение: этот предмет был выбран # 1 Самый ужасный хак 2008 года , поэтому используйте его с осторожностью. На самом деле, избегайте этого, как чумы, но это, безусловно, скрытый рубин.

Суператоры добавляют новых операторов в Ruby

Вы когда-нибудь хотели сверхсекретного оператора рукопожатия для какой-то уникальной операции в вашем коде? Как играть в код-гольф? Попробуйте операторы, такие как - ~ + ~ - или <--- Последний из них используется в примерах для изменения порядка элементов.

Я не имею ничего общего с проектом Superators Project, кроме того, что восхищаюсь им.


19

Я опаздываю на вечеринку, но:

Вы можете легко взять два массива одинаковой длины и превратить их в хеш, один из которых содержит ключи, а другой - значения:

a = [:x, :y, :z]
b = [123, 456, 789]

Hash[a.zip(b)]
# => { :x => 123, :y => 456, :z => 789 }

(Это работает, потому что Array # zip "архивирует" значения из двух массивов:

a.zip(b)  # => [[:x, 123], [:y, 456], [:z, 789]]

И Hash [] может взять именно такой массив. Я видел, как люди это делают:

Hash[*a.zip(b).flatten]  # unnecessary!

Что дает тот же результат, но сплат и сплющение совершенно не нужны - возможно, их не было в прошлом?)


3
Это было действительно недокументированным долгое время (см. Redmine.ruby-lang.org/issues/show/1385 ). Обратите внимание, что эта новая форма является новой для Ruby 1.8.7
Marc-André Lafortune

19

Авто-живительные хэши в Ruby

def cnh # silly name "create nested hash"
  Hash.new {|h,k| h[k] = Hash.new(&h.default_proc)}
end
my_hash = cnh
my_hash[1][2][3] = 4
my_hash # => { 1 => { 2 => { 3 =>4 } } }

Это может быть чертовски удобно.


1
Я бы обернул его в модуль, чтобы у меня было такое же чувство, как у родного хеша init:module InfHash; def self.new; Hash.new {|h,k| h[k] = Hash.new(&h.default_proc)}; end; end
asaaki

16

Уничтожение массива

(a, b), c, d = [ [:a, :b ], :c, [:d1, :d2] ]

Куда:

a #=> :a
b #=> :b
c #=> :c
d #=> [:d1, :d2]

Используя эту технику, мы можем использовать простое присваивание, чтобы получить нужные значения из вложенного массива любой глубины.


15

Class.new()

Создайте новый класс во время выполнения. Аргумент может быть производным классом, а блок является телом класса. Возможно, вы также захотите посмотреть, как const_set/const_get/const_defined?правильно зарегистрировать новый класс, чтобыinspect печатать имя вместо числа.

Не то, что вам нужно каждый день, но довольно удобно, когда вы делаете.


1
MyClass = Class.new Array do; def hi; 'hi'; end; endкажется эквивалентным class MyClass < Array; def hi; 'hi'; end; end.
yfeldblum

1
Вероятно, более правдиво, чем я думал. Даже кажется, что вы можете наследовать от переменной, а не только от константы. Однако версия с сахаром (секунда) не работает, если вам нужно создать имя класса во время выполнения. (Обнажая Эвала, конечно.)
Джастин Лав

Эта техника довольно хорошо описана в книге Метапрограммирование Ruby .
Пол Пладий

13

создать массив последовательных чисел:

x = [*0..5]

устанавливает х в [0, 1, 2, 3, 4, 5]


Да, но это не так коротко и сладко;)
скакун

2
краткость объективна, читабельность - дело вкуса и опыта
Алексей

Оператор splat ( *) в основном вызывает в to_aлюбом случае.
Матеус Морейра

13

Большая магия, которую вы видите в Rubyland, связана с метапрограммированием, которое заключается в простом написании кода, который пишет код для вас. Ruby's attr_accessor, attr_readerи attr_writerвсе это простое метапрограммирование, поскольку они создают два метода в одной строке, следуя стандартному шаблону. Rails делает много метапрограммирования со своими методами управления отношениями, такими как has_oneиbelongs_to .

Но довольно просто создать свои собственные приемы метапрограммирования, используя class_evalдля выполнения динамически написанного кода.

В следующем примере объект-оболочка позволяет перенаправлять определенные методы во внутренний объект:

class Wrapper
  attr_accessor :internal

  def self.forwards(*methods)
    methods.each do |method|
      define_method method do |*arguments, &block|
        internal.send method, *arguments, &block
      end
    end
  end

  forwards :to_i, :length, :split
end

w = Wrapper.new
w.internal = "12 13 14"
w.to_i        # => 12
w.length      # => 8
w.split('1')  # => ["", "2 ", "3 ", "4"]

Метод Wrapper.forwardsпринимает символы для имен методов и сохраняет их в methodsмассиве. Затем для каждого из указанных мы используемdefine_method создание нового метода, задачей которого является отправка сообщения, включая все аргументы и блоки.

Большой ресурс для вопросов метапрограммирования - « Почему Лаки Стифф« Видит метапрограммирование ясно » .


Я хочу сначала погрузиться в метапрограммирование в ruby. Не могли бы вы предоставить некоторые ссылки для начала работы (кроме указанной ссылки)? Книги тоже подойдут. Спасибо.
Chirantan

Videocasting серия PragProg в «The Ruby Object Model и Метапрограммирование» его хорошее введение в мета - программирование с использованием рубина: pragprog.com/screencasts/v-dtrubyom/...
caffo


12

используйте все, что отвечает ===(obj)для сравнения случаев:

case foo
when /baz/
  do_something_with_the_string_matching_baz
when 12..15
  do_something_with_the_integer_between_12_and_15
when lambda { |x| x % 5 == 0 }
  # only works in Ruby 1.9 or if you alias Proc#call as Proc#===
  do_something_with_the_integer_that_is_a_multiple_of_5
when Bar
  do_something_with_the_instance_of_Bar
when some_object
  do_something_with_the_thing_that_matches_some_object
end

Module(и, таким образом Class) Regexp,Date , и многие другие классы определяют метод экземпляра: === (другой), и все они могут быть использованы.

Спасибо Фаррелу за напоминание о Proc#callпсевдониме, как Proc#===в Ruby 1.9.


11

«Рубиновый» бинарный файл (по крайней мере, MRI) поддерживает множество переключателей, которые сделали Perl One-Liner довольно популярным.

Значительные из них:

  • -n Устанавливает внешний цикл с помощью «get», который волшебным образом работает с данным именем файла или STDIN, устанавливая каждую строку чтения в $ _
  • -p Аналогично -n, но с автоматическим puts в конце каждой итерации цикла
  • -a Автоматический вызов .split в каждой строке ввода, хранится в $ F
  • -i Редактирование входных файлов на месте
  • -l Автоматический вызов .chomp при вводе
  • -e выполнить кусок кода
  • -c Проверить исходный код
  • -w с предупреждениями

Некоторые примеры:

# Print each line with its number:
ruby -ne 'print($., ": ", $_)' < /etc/irbrc

# Print each line reversed:
ruby -lne 'puts $_.reverse' < /etc/irbrc

# Print the second column from an input CSV (dumb - no balanced quote support etc):
ruby -F, -ane 'puts $F[1]' < /etc/irbrc

# Print lines that contain "eat"
ruby -ne 'puts $_ if /eat/i' < /etc/irbrc

# Same as above:
ruby -pe 'next unless /eat/i' < /etc/irbrc

# Pass-through (like cat, but with possible line-end munging):
ruby -p -e '' < /etc/irbrc

# Uppercase all input:
ruby -p -e '$_.upcase!' < /etc/irbrc

# Same as above, but actually write to the input file, and make a backup first with extension .bak - Notice that inplace edit REQUIRES input files, not an input STDIN:
ruby -i.bak -p -e '$_.upcase!' /etc/irbrc

Не стесняйтесь гуглить "ruby one-liners" и "perl one-liners" для множества более полезных и практических примеров. По сути, это позволяет вам использовать ruby ​​как довольно мощную замену awk и sed.


10

Метод send () - это метод общего назначения, который можно использовать с любым классом или объектом в Ruby. Если не переопределено, send () принимает строку и вызывает имя метода, для которого она передана. Например, если пользователь нажимает кнопку «Clr», строка «press_clear» будет отправлена ​​методу send () и будет вызван метод «press_clear». Метод send () позволяет весело и динамично вызывать функции в Ruby.

 %w(7 8 9 / 4 5 6 * 1 2 3 - 0 Clr = +).each do |btn|
    button btn, :width => 46, :height => 46 do
      method = case btn
        when /[0-9]/: 'press_'+btn
        when 'Clr': 'press_clear'
        when '=': 'press_equals'
        when '+': 'press_add'
        when '-': 'press_sub'
        when '*': 'press_times'
        when '/': 'press_div'
      end

      number.send(method)
      number_field.replace strong(number)
    end
  end

Я больше говорю об этой функции в Blogging Shoes: Приложение Simple-Calc


Звучит как отличный способ открыть дыру в безопасности.
мП

4
Я бы использовал символы везде, где это возможно.
Рето

9

Обмани некоторый класс или модуль, говорящий, что он требует чего-то, что ему действительно не нужно:

$" << "something"

Это полезно, например, когда требуется A, который, в свою очередь, требует B, но нам не нужен B в нашем коде (и A не будет использовать его в нашем коде):

Например, Backgroundrb bdrb_test_helper requires 'test/spec', но вы не используете его вообще, так что в вашем коде:

$" << "test/spec"
require File.join(File.dirname(__FILE__) + "/../bdrb_test_helper")

Устраняет ли это проблемы, когда gem A требует foo-1.0.0, а gem B требует foo-1.0.1?
Эндрю Гримм

Нет, потому что код «что-то» не будет доступен: это только имитирует, что «что-то» требуется, но на самом деле это не требуется. $ "- это массив, содержащий имена модулей, загруженных с помощью require (он используется by require, чтобы предотвратить загрузку модулей дважды). Поэтому, если вы используете это, чтобы обмануть драгоценные камни, это вызовет сбой, когда драгоценные камни попытаются использовать фактическое" что-то " код, потому что он не существует. Вместо этого вы можете захотеть принудительно загрузить конкретную версию гема (например, foo-1.0.0) вместо последней: docs.rubygems.org/read/chapter/4#page71
olegueret

9

Fixnum#to_s(base)может быть действительно полезным в некоторых случаях. Одним из таких случаев является генерация случайных (псевдо) уникальных токенов путем преобразования случайного числа в строку, используя основание 36.

Токен длины 8:

rand(36**8).to_s(36) => "fmhpjfao"
rand(36**8).to_s(36) => "gcer9ecu"
rand(36**8).to_s(36) => "krpm0h9r"

Жетон длины 6:

rand(36**6).to_s(36) => "bvhl8d"
rand(36**6).to_s(36) => "lb7tis"
rand(36**6).to_s(36) => "ibwgeh"

9

Определение метода, который принимает любое количество параметров и просто отбрасывает их все

def hello(*)
    super
    puts "hello!"
end

Вышеупомянутый helloметод должен только puts "hello"отображаться на экране и вызываться super- но поскольку суперкласс helloопределяет параметры, он также должен это делать - однако поскольку ему фактически не нужно использовать сами параметры - ему не нужно давать им имя.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.