Что такое "главное" в Ruby?

Если я запускаю этот файл как "ruby x.rb":

class X
end
x = X.new

Что это за вещь, которая зоветX.new"?

Это объект / процесс / и т. Д.?

6 ответов

Решение

Все в Ruby происходит в контексте какого-либо объекта. Объект на верхнем уровне называется "основным". По сути, это экземпляр Object со специальным свойством, которое любые методы, определенные там, добавляются как методы экземпляра Object (поэтому они доступны везде).

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

puts object_id
@a = 'Look, I have instance variables!'
puts @a

и он напечатает "105640" и "Смотри, у меня есть переменные экземпляра!".

Это не то, что вам обычно нужно беспокоиться, но оно есть.

Вызывающий объект верхнего уровня - это объект main, который относится к классу Object.

Попробуйте эту программу ruby:

p self
p self.class

Это Х класс. Вы вызываете метод "new", который создает объект класса X. Итак, если вы запустите этот текст как скрипт, Ruby:

  • создает новый класс X, который является подклассом Object, и который автоматически (как подкласс Object) наследует некоторые методы, из которых new это один.
  • устанавливает имя x
  • вызывает new метод на этом новом классе Xсоздание объекта экземпляра X; х получает ссылку на этот объект.

Это рубиновый переводчик

x = X.new

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

объект, в контексте которого выполняется код верхнего уровня. Это означает, что на верхнем уровне ссылается на объект:

      $ ruby -e 'p self'
main

И этоrubyследует за цепочкой поиска методов, чтобы определить, какой метод вызывать:

      $ ruby -e 'p singleton_class.ancestors'
[#<Class:#<Object:0x00007f9e9fdee230>>, Object, Kernel, BasicObject]

Может быть больше, но это то, что вы получаете с самого начала.

сам является экземпляром:

      $ ruby -e 'p self.class'
Object

Он имеет одноэлементный класс с двумя методами (метод и псевдоним, если быть более точным):

      $ ruby -e 'p singleton_class.instance_methods(false)'
[:inspect, :to_s]
$ ruby -e 'p singleton_methods'
[:inspect, :to_s]

Это определено здесь.

Как видите егоto_sметод возвращает"main"(переопределяет поведение ), что вы получаете, когда делаетеp self.

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

      main = Object.new
class Object
  def main.go
    <your code here>
  end
end
main.go

Это грубая идея. Позвольте мне обосновать это в нескольких шагах.

В Ruby вы можете на самом деле вкладывать методы, но каждый раз, когда вы вызываете внешний метод, внутренний определяется/переопределяется. Что еще более важно, он определен как метод экземпляра окружающего класса:

      class A
  def m
    def m2; end
  end
end
A.new.m
p A.instance_methods(false)  # [:m2, :m]

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

      class A
  class << self
    def m
      def m2; end
    end
  end
end
A.m
p A.singleton_class.instance_methods(false)  # [:m2, :m]

А что, если мы воспользуемсяdef self.<name>обозначение?

      class A
  def self.m
    def m2; end
  end
end
A.m
p A.singleton_class.instance_methods(false)  # [:m]
p A.instance_methods(false)                  # [:m2]

Так,self.влияет толькоm,m2становится методом экземпляраA.

На самом деле вместо может быть какой-то случайный объект:

      o = Object.new
A = Class.new do
  def o.m
    def m2; end
  end
end
o.m
p o.singleton_class.instance_methods(false)  # [:m]
p A.instance_methods(false)                  # [:m2]

я должен был использоватьClass.newпотому что сclass oне будет виден внутри определения класса.

Или на самом деле у меня не было:

      class A
  o = Object.new
  def o.m
    def m2; end
  end
  o.m
  p o.singleton_class.instance_methods(false)  # [:m]
  p A.instance_methods(false)                  # [:m2]
end

Но давайте проигнорируем эту ветвь мысли.

Пара изменений, и вы получите это:

      main = Object.new
Object.class_eval do
  def main.go
    @a = 1
    def m2
      puts @a
    end
    m2                            # 1
  end
end
main.go
p Object.instance_methods(false)  # [:m2]
p main.instance_variables         # [:@a]

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

Вы также можете добавить:

      def main.to_s
  "main"
end
main.instance_eval { alias inspect to_s }

для полноты.

Другой способ — использовать глобальные переменные:

      $main = Object.new
class Object
  def $main.go
    @a = 1
    def m2
      puts @a
    end
    m2                            # 1
  end
end
$main.go
p Object.instance_methods(false)  # [:m2]
p $main.instance_variables        # [:@a]

Конечно переменные /$mainиgoметод не существует. Но больше никаких недостатков не приходит на ум, когда я думаю об этой идее. Идея в том, что это работает так, как если бы ваш код помещался в метод и выполнялся при запуске метода.

Также это объясняет, почему методы, определенные на верхнем уровне, видны везде:

a.rb:

      f
      $ ruby -e 'def f; puts "f"; end; require "./a"'
f

Поскольку они становятся методами экземпляра .

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

UPD Я заметил, что нельзя определять константы (как обычно), классы и модули вmain.go. Таким образом, абстракция кажется негерметичной. Я мог бы попытаться изменить его:

      Object.class_eval do
  <your constants, classes, modules, methods>
  def main.go
    <the rest of the code>
  end
end

Но на данный момент я бы скорее сказал, что на высшем уровнеselfуказывает наmainобъект, а текущая ссылка класса наObjectсорт. Подробнее о ссылках на классы здесь.

Как сказал Чарли Мартин, X.new - это вызов конструктора класса X, который возвращает объект типа X, хранящийся в переменной x.

Исходя из вашего названия, я думаю, что вы ищете немного больше. Ruby не нуждается в main, он выполняет код в том порядке, в котором он его видит. Таким образом, зависимости должны быть включены до их вызова.

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

Другие вопросы по тегам