Cet article est un article qui redéfinit la méthode Float (arg, exception: true)
en utilisant builtin introduit dans l'article suivant que j'ai écrit plus tôt.
J'ai implémenté Ruby avec Ruby (et C) (j'ai joué avec builtin)
Ce que vous faites est à peu près le même que dans l'article précédent. La différence par rapport à la dernière fois est que j'ai essayé de redéfinir la méthode qui peut être appelée n'importe où avec builtin au lieu de la méthode d'une classe spécifique.
Après avoir écrit l'article précédent, je me demandais si des méthodes telles que rise
et Float
pouvaient prendre en charge la fonction intégrée, alors j'ai eu envie de l'essayer.
De la conclusion, il semble que de telles méthodes globales puissent également être supportées par builtin (bien que cela puisse ne pas être recommandé car nous n'avons pas confirmé s'il s'agit du comportement prévu ...)
Builtin est d'implémenter Ruby lui-même dans Ruby (et C). Pour plus de détails, veuillez lire l'article précédent.
J'ai implémenté Ruby avec Ruby (et C) (j'ai joué avec builtin)
J'utilise l'environnement de l'article précédent tel quel. Il n'y a pas de changements majeurs.
J'ai implémenté Ruby avec Ruby (et C) (j'ai joué avec builtin)
Tout d'abord, recherchez la source qui implémente la méthode Float
. Les méthodes Ruby sont généralement définies comme ceci.
rb_define_method(rb_mKernel, "to_s", rb_any_to_s, 0);
Le premier argument est la variable de classe / module dans laquelle la méthode est définie. Généralement, il est créé avec un nom de classe ou un nom de module tel que rb_cArray
. Le deuxième argument, «to_s», est le nom de la méthode appelée côté Ruby. Le troisième argument est une fonction C qui est exécutée lorsque la méthode est appelée, et le dernier argument spécifie le nombre d'arguments que la méthode reçoit.
Cette fois, nous voulons réimplémenter la méthode Float
, donc cherchons le code source Ruby avec git grep \" Float \ "ʻetc. Ensuite, il semble qu'il soit implémenté comme suit dans ʻobject.c
.
rb_define_global_function("Float", rb_f_float, -1);
Réimplémentons-le.
Dans l'article précédent, nous avons réimplémenté Hash # delete
. Aussi, pour prendre en charge intégré, j'ai modifié common.mk
etc.
Cependant, dans le code maître Ruby actuel, Kernel # clone
, qui a été implémenté dans ʻobject.c`, est rendu compatible avec builtin, il n'est donc pas nécessaire de les modifier.
Deux sources doivent être modifiées: ʻobject.c et
kernel.rb`.
Dans ʻobject.c, supprimez d'abord le code suivant qui définit la méthode
Float`.
void
InitVM_Object(void)
{
Init_class_hierarchy();
//réduction
- rb_define_global_function("Float", rb_f_float, -1);
//réduction
}
Ensuite, modifiez le rb_f_float
qui définit le traitement de la méthode Float
comme suit.
- /*
- * 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));
}
Puisqu'il utilise intégré et reçoit des arguments de mot-clé, il est défini sur VALUE main, VALUE arg, VALUE opts
au lieu de ʻint argc, VALUE * argv`. De plus, «VALUE main» est ajouté pour prendre l'argument que la méthode globale reçoit implicitement à la place.
Ceci termine la modification dans ʻobject.c`!
Dans Ruby, les méthodes globales qui peuvent être appelées de n'importe où peuvent être redéfinies dans le module Kernel
.
Par exemple, la méthode «put» peut être modifiée comme suit:
module Kernel
def puts *args
p "hoge"
end
end
puts :ho
#=> "hoge"Est affiché
Utilisez ceci pour ajouter la méthode Float
dans kernel.rb
comme indiqué ci-dessous.
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
Le rb_f_float
qui a été rendu compatible avec Builtin plus tôt est appelé avec __builtin_rb_f_float
. Maintenant, la modification dans kernel.rb
est OK.
Après cela, si vous pouvez construire en exécutant make
et make install
, l'implémentation avec builtin est terminée.
Il semble que des méthodes telles que Float
et ʻInteger` peuvent être intégrées comme ceci. Indépendamment du fait qu'il ait été envoyé sous forme de patch, il était intéressant qu'il semble être tout à fait applicable.
En passant, lorsque j'ai essayé les benchmarks suivants, j'ai senti que je pouvais m'attendre à une amélioration des performances.
benchmark:
float: "Float(42)"
float_true: "Float(42, exception: true)"
float_false: "Float(42, exception: false)"
loop_count: 10000
Résultats ci-dessous
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
Si une méthode telle que Float
est intégrée, je pense que la maintenabilité du code sera plus importante que les performances.
Recommended Posts