Ich habe versucht, Ruby's Float (arg, Ausnahme: true) mit Builtin neu zu implementieren

Einführung

Dieser Artikel ist ein Artikel, der die Methode "Float (arg, Ausnahme: true)" unter Verwendung der in dem folgenden Artikel, den ich zuvor geschrieben habe, eingeführten Methode neu definiert.

Ich habe Ruby mit Ruby (und C) implementiert (ich habe mit Builtin gespielt)

Was Sie tun, ist ungefähr das gleiche wie im vorherigen Artikel. Der Unterschied zum letzten Mal besteht darin, dass ich versucht habe, eine Methode neu zu definieren, die überall mit eingebauten Methoden anstelle einer Methode einer bestimmten Klasse aufgerufen werden kann.

Nachdem ich den vorherigen Artikel geschrieben hatte, fragte ich mich, ob Methoden wie "Raise" und "Float" Builtin unterstützen könnten, also wollte ich es ausprobieren.

Aus der Schlussfolgerung geht hervor, dass solche globalen Methoden auch durch eingebaute Methoden unterstützt werden können (obwohl dies möglicherweise nicht empfohlen wird, da wir nicht bestätigt haben, ob es sich um das beabsichtigte Verhalten handelt ...).

Was ist eingebaut?

Builtin ist die Implementierung von Ruby selbst in Ruby (und C). Für Details lesen Sie bitte den vorherigen Artikel.

Ich habe Ruby mit Ruby (und C) implementiert (ich habe mit Builtin gespielt)

Umgebung

Ich verwende die Umgebung des vorherigen Artikels so wie sie ist. Es gibt keine wesentlichen Änderungen.

Ich habe Ruby mit Ruby (und C) implementiert (ich habe mit Builtin gespielt)

Ich versuchte es

Suchen Sie den Implementierungsort der Float-Methode

Suchen Sie zunächst die Quelle, die die Float-Methode implementiert. Ruby-Methoden werden im Allgemeinen so definiert.

rb_define_method(rb_mKernel, "to_s", rb_any_to_s, 0);

Das erste Argument ist die Klassen- / Modulvariable, in der die Methode definiert ist. Im Allgemeinen wird es mit einem Klassennamen oder Modulnamen wie "rb_cArray" erstellt. Das zweite Argument, "to_s", ist der Name der Methode, die auf der Ruby-Seite aufgerufen wird. Das dritte Argument ist eine C-Funktion, die beim Aufruf der Methode ausgeführt wird, und das letzte Argument gibt die Anzahl der Argumente an, die die Methode empfängt.

Dieses Mal möchten wir die Float-Methode erneut implementieren. Suchen wir also den Ruby-Quellcode mit "git grep" Float "usw. Dann scheint es, dass es wie folgt in object.c implementiert ist.

rb_define_global_function("Float", rb_f_float, -1);

Lassen Sie es uns erneut implementieren.

Neuimplementierung der Float-Methode

Im vorherigen Artikel haben wir "Hash # delete" erneut implementiert. Um die integrierte Funktion zu unterstützen, habe ich außerdem "common.mk" usw. geändert. Im aktuellen Ruby-Mastercode ist jedoch "Kernel # clone", der in "object.c" implementiert wurde, mit dem integrierten Code kompatibel, sodass keine Änderungen erforderlich sind.

Es gibt zwei Quellen, die geändert werden müssen: object.c und kernel.rb.

Ändern Sie object.c

Löschen Sie in object.c zuerst den folgenden Code, der die Float -Methode definiert.

void
InitVM_Object(void)
{
    Init_class_hierarchy();

    //Kürzung
-    rb_define_global_function("Float", rb_f_float, -1);
    //Kürzung
}

Ändern Sie als Nächstes das "rb_f_float", das die Verarbeitung der "Float" -Methode wie folgt definiert.

- /*	
-  *  call-seq:	
-  *     Float(arg, exception: true)    -> float or nil	
-  *	
-  *  Returns <i>arg</i> converted to a float. Numeric types are	
-  *  converted directly, and with exception to String and	
-  *  <code>nil</code> the rest are converted using	
-  *  <i>arg</i><code>.to_f</code>.  Converting a String with invalid	
-  *  characters will result in a ArgumentError.  Converting	
-  *  <code>nil</code> generates a TypeError.  Exceptions can be	
-  *  suppressed by passing <code>exception: false</code>.	
-  *	
-  *     Float(1)                 #=> 1.0	
-  *     Float("123.456")         #=> 123.456	
-  *     Float("123.0_badstring") #=> ArgumentError: invalid value for Float(): "123.0_badstring"	
-  *     Float(nil)               #=> TypeError: can't convert nil into Float	
-  *     Float("123.0_badstring", exception: false)  #=> nil	
-  */	

static VALUE	static VALUE
- rb_f_float(int argc, VALUE *argv, VALUE obj)
+ rb_f_float(rb_execution_context_t *ec, VALUE main, VALUE arg, VALUE opts)
{
-    VALUE arg = Qnil, opts = Qnil;	
-
-    rb_scan_args(argc, argv, "1:", &arg, &opts);	
-    return rb_convert_to_float(arg, opts_exception_p(opts));	    
+   return rb_convert_to_float(arg, opts_exception_p(opts));
}

Da es integrierte Schlüsselwortargumente verwendet und diese empfängt, wird es auf "VALUE main, VALUE arg, VALUE opts" anstelle von "int argc, VALUE * argv" gesetzt. Außerdem wird "VALUE main" hinzugefügt, um das Argument zu übernehmen, das die globale Methode stattdessen implizit empfängt.

Damit ist die Änderung in object.c abgeschlossen!

Ändern Sie kernel.rb

In Ruby können globale Methoden, die von überall aufgerufen werden können, im Kernel-Modul neu definiert werden.

Zum Beispiel kann die "Puts" -Methode wie folgt mit Affen gepatcht werden:

module Kernel
   def puts *args
      p "hoge" 
   end
end


puts :ho
#=> "hoge"Wird angezeigt

Verwenden Sie diese Option, um die Float -Methode in kernel.rb hinzuzufügen, wie unten gezeigt.

module Kernel
+ 
+   #
+   #  call-seq:
+   #     Float(arg, exception: true)    -> float or nil
+   #
+   #  Returns <i>arg</i> converted to a float. Numeric types are
+   #  converted directly, and with exception to String and
+   #  <code>nil</code> the rest are converted using
+   #  <i>arg</i><code>.to_f</code>.  Converting a String with invalid
+   #  characters will result in a ArgumentError.  Converting
+   #  <code>nil</code> generates a TypeError.  Exceptions can be
+   #  suppressed by passing <code>exception: false</code>.
+   #
+   #     Float(1)                 #=> 1.0
+   #     Float("123.456")         #=> 123.456
+   #     Float("123.0_badstring") #=> ArgumentError: invalid value for Float(): "123.0_badstring"
+   #     Float(nil)               #=> TypeError: can't convert nil into Float
+   #     Float("123.0_badstring", exception: false)  #=> nil
+   #
+   def Float(arg, exception: true)
+     __builtin_rb_f_float(arg, exception)
+   end
+ 
  #
  #  call-seq:
  #     obj.clone(freeze: nil) -> an_object
  #
  #  Produces a shallow copy of <i>obj</i>---the instance variables of
  #  <i>obj</i> are copied, but not the objects they reference.
  #  #clone copies the frozen value state of <i>obj</i>, unless the
  #  +:freeze+ keyword argument is given with a false or true value.
  #  See also the discussion under Object#dup.
  #
  #     class Klass
  #        attr_accessor :str
  #     end
  #     s1 = Klass.new      #=> #<Klass:0x401b3a38>
  #     s1.str = "Hello"    #=> "Hello"
  #     s2 = s1.clone       #=> #<Klass:0x401b3998 @str="Hello">
  #     s2.str[1,4] = "i"   #=> "i"
  #     s1.inspect          #=> "#<Klass:0x401b3a38 @str=\"Hi\">"
  #     s2.inspect          #=> "#<Klass:0x401b3998 @str=\"Hi\">"
  #
  #  This method may have class-specific behavior.  If so, that
  #  behavior will be documented under the #+initialize_copy+ method of
  #  the class.
  #
  def clone(freeze: nil)
    __builtin_rb_obj_clone2(freeze)
  end
end

Das rb_f_float, das zuvor mit Builtin kompatibel gemacht wurde, wird mit __builtin_rb_f_float aufgerufen. Jetzt ist die Änderung in kernel.rb OK.

Schließlich

Wenn Sie danach durch Ausführen von "make" und "make install" erstellen können, ist die Implementierung mit integriertem Code abgeschlossen.

abschließend

Es scheint, dass Methoden wie Float und Integer so eingebaut werden können. Unabhängig davon, ob es als Patch gesendet wurde, war es interessant, dass es durchaus anwendbar zu sein schien.

Übrigens, als ich die folgenden Benchmarks ausprobierte, hatte ich das Gefühl, dass ich eine Leistungsverbesserung erwarten könnte.

benchmark:
  float: "Float(42)"
  float_true: "Float(42, exception: true)"
  float_false: "Float(42, exception: false)"
loop_count: 10000

Ergebnisse unten

Calculating -------------------------------------
                     compare-ruby  built-ruby
               float      37.495M     15.878M i/s -     10.000k times in 0.000267s 0.000630s
          float_true       1.109M      1.211M i/s -     10.000k times in 0.009015s 0.008261s
         float_false       1.227M      1.191M i/s -     10.000k times in 0.008150s 0.008395s

Comparison:
                            float
        compare-ruby:  37495314.0 i/s
          built-ruby:  15878056.0 i/s - 2.36x  slower

                       float_true
          built-ruby:   1210507.2 i/s
        compare-ruby:   1109250.0 i/s - 1.09x  slower

                      float_false
        compare-ruby:   1226948.7 i/s
          built-ruby:   1191128.5 i/s - 1.03x  slower

Wenn eine Methode wie "Float" eingebaut ist, ist die Wartbarkeit des Codes meiner Meinung nach wichtiger als die Leistung.

Recommended Posts

Ich habe versucht, Ruby's Float (arg, Ausnahme: true) mit Builtin neu zu implementieren
Ich habe DI mit Ruby versucht
Ich habe versucht, das Problem der "mehrstufigen Auswahl" mit Ruby zu lösen
Ich habe versucht, mit Web Assembly zu beginnen
Ich habe versucht, das Problem der Tribonacci-Sequenz in Ruby mit Wiederholung zu lösen.
Ich habe versucht, eine Standardauthentifizierung mit Java durchzuführen
Ich habe versucht, die Federbeinkonfiguration mit Coggle zu verwalten
Ich habe versucht, Anmeldeinformationen mit JMX zu verwalten
Ich habe versucht, Ruby mit Ruby (und C) zu implementieren (ich habe mit Builtin gespielt)
Ich habe mit Ruby einen Blackjack gemacht (ich habe versucht, Minitest zu verwenden)
[Ruby-Grundlagen] Ich habe versucht, Module zu lernen (Kapitel 1)
Ich habe versucht, den Block mit Java zu brechen (1)
Ich habe versucht, was ich mit Stream leise versuchen wollte.
Ich habe versucht, das Hochladen von Dateien mit Spring MVC zu implementieren
Ich habe versucht, TCP / IP + BIO mit JAVA zu implementieren
Ich habe MySQL 5.7 mit Docker-Compose gestartet und versucht, eine Verbindung herzustellen
Ich habe versucht, mit Spring Data JPA zu beginnen
Ich habe versucht, Animationen mit der Blazor + Canvas-API zu zeichnen
Ich habe versucht, Sterling Sort mit Java Collector zu implementieren
Ich habe versucht, mit Chocolatey eine Java8-Entwicklungsumgebung zu erstellen
Ich habe versucht, eine Java EE-Anwendung mit OpenShift zu modernisieren.
Ich habe versucht, die Verarbeitungsgeschwindigkeit mit spiritueller Technik zu erhöhen
[Rails] Ich habe versucht, eine Mini-App mit FullCalendar zu erstellen
Ich habe die grundlegende Grammatik von Ruby kurz zusammengefasst
Ich habe versucht, den Chat mit dem Minecraft-Server mit der Discord-API zu verknüpfen
[Rails] Ich habe versucht, die Stapelverarbeitung mit der Rake-Task zu implementieren
Selbst in Java möchte ich true mit == 1 && a == 2 && a == 3 ausgeben
Ich habe versucht, mit Docker eine Padrino-Entwicklungsumgebung zu erstellen
Ich habe versucht, mit Swagger mit Spring Boot zu beginnen
Ich habe versucht, mit Ractor mehrere Objekte übergeben zu können
Ich habe UPSERT mit PostgreSQL ausprobiert.
Ich habe BIND mit Docker ausprobiert
Ich habe versucht, yum-cron zu verifizieren
Ich möchte eine Browsing-Funktion mit Ruby on Rails hinzufügen
Ich habe versucht, mit Docker eine Plant UML Server-Umgebung zu erstellen
Ich habe versucht, mithilfe von JDBC Template mit Spring MVC eine Verbindung zu MySQL herzustellen
Ich habe versucht, Code wie eine Typdeklaration in Ruby zu schreiben
[Rubiy] Heute Abend habe ich versucht, die Schleifenverarbeitung zusammenzufassen [Zeiten, Pause ...]
Ich habe versucht, die Bildvorschau mit Rails / jQuery zu implementieren
Ich habe versucht, mit Eclipse + Tomcat eine http2-Entwicklungsumgebung zu erstellen
Ich habe versucht, eine flexible ODER-Zuordnung mit MyBatis Dynamic SQL zu implementieren
Ich habe versucht, eine Android-Anwendung mit MVC zu erstellen (Java)
Ich habe versucht, den Betrieb des gRPC-Servers mit grpcurl zu überprüfen
Ich habe versucht, einen Numeron zu erstellen, der mit Ruby nicht gut ist
Ich habe versucht, mit Rails eine Gruppenfunktion (Bulletin Board) zu erstellen
Ich habe versucht, C # (Indexer) zu kauen.
Ich habe versucht, JOOQ mit Gradle zu verwenden
Ich habe eine morphologische Analyse mit MeCab versucht