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 ...).
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)
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)
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.
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
.
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!
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.
Wenn Sie danach durch Ausführen von "make" und "make install" erstellen können, ist die Implementierung mit integriertem Code abgeschlossen.
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