Guide de survie pour Ruby
Tout d'abord, une url avec des exemples sur les fonctionnalités de ruby (du genre à garder sous le main) :- Projet ruby-kickstart.com (ancienne version locale ensta au cas où...)
- exemples pour array.rb (Fouiller l'arborescence pour avoir d'autres exemples)
Premier contact avec Ruby
Tout est objet en Ruby même les nombres (Fixnum, ...). L'approche objet est très agréable d'utilisation : on enchaine les opérations de manière naturelle, un peu comme des filtres unix avec des "pipes". Le résultat d'un appel est un objet, qui dispose donc de méthodes :# Tout est objet, même les nombres: -199.abs # 199 "ruby is cool".length # 12 "Rick".index("c") # 2 "Nice Day Isn't It?".downcase.split(//).sort.uniq.join # " '?acdeinsty"Pour en savoir plus sur les chaînes Ruby, pour pouvez utiliser la commande unix ri (pour Ruby Info, l'équivalent du man unix).
Même les boucles ne sont que des cas particuliers d'une notion plus générale appelée Itérateur (bien plus simple à exploiter qu'en C++ ou en Java)
for i in myList do puts "i=" + i endN'est qu'une facilité syntaxique pour ecrire :
myList.each{ | i | puts "i=" + i}Les collections ruby (Array, ...) proposent de nombreux itérateurs, et on peut en définir de nouveaux. De plus, toute classe peut-être réouverte et complétée.
Vous pouvez lancer l'interpréteur interactif Ruby :
irbpuis tapez l'exemple suivant :
class Integer def fact if self <= 1 then 1 else self * (self-1).fact end end end # Test par . 5.fact => 120 # Mais 120 est aussi un Fixnum. Donc on peut faire : 5.fact.fact => 6689502913449127057588118054090372586752746333138029810295671352301633 5572449629893668741652719849813081576378932140905525344085894081218598 98481114389650005964960521256960000000000000000000000000000Il y a de nombreuses façons d'écrire du code Ruby. Voici une version C like de la méthode ci-dessus :
class Integer def fact() self <= 1 ? 1 : self*(self-1).fact end endUne version par itérateur (mais pas forcément plus lisible si on ne connait pas "inject" !) donnerait :
class Integer def fact (2..self).inject(1) { |f, n| f * n } end endPrenez bien conscience dans cet exemple simple, que vous n'avez pas créé une nouvelle fonction ou classe personnelle, mais que vous venez de compléter une classe Ruby que vous n'avez pas écrite !
Les bases
Les variables
@@variable_de_classe @variable_d_instance variable_locale $variable_globale (à éviter ;-) UneConstante
Les chaines de caractères et les symboles
"Une_chaine" est un objet de la classe String qui a donc ses méthodes et peut etre éventuellement modifiéestr = "abcdefghij" str[2..3] = str[2..8].swapcase.reverse str => "abIHGFEDCefghij"Un "symbole" ressemble à une chaine, mais il est représenté en mémoire par un entier unique, souvent utilisé comme clés dans une table de hachage, ou comme nom d'option dans un programme.
opts[:une_option] = 999 but = TkButton.new(coolingFrame, :text => "Calculer \"temp_init\"" , :command => proc {self.calcul_temp_init} , :font => "Helvetica 14" )On peut facilement passer de l'un à l'autre voire utiliser l'un pour l'autre par my_str.to_sym ou :my_symbol.to_s.
Création d'une table de hachage
@@default_opts = { # Type de refroidissement : constantes REFROID_GEO et REFROID_LOG :cooling_type => AnnealingAlgo::REFROID_GEO, # Raison de la suite geometrique des temperatures # (utile seulement si cooling_type==REFROID_GEO) :geo_coeff => 0.9, :solver_title => "Recuit simulé", :prefs_title => "Préférence pour le Recuit Simulé" }
Les structures de controles
les tests et les booléens
Tout objet peut faire l'objet de tests, mais les seuls objets suivants représente la valeur false : - la constante false (singleton d ela classe - la valeur nil (objet non déclaré, ou non initialisé) En particulier les valeurs suivantes sont considérées comme true (grosse différence avec Perl, Php et autres langages incidieux) - les nombres 0, 0.0, 0x0 - les chaines de caractères "", "0", "false", ... # Le test tout simple if bool_expr some_code end # Structure générale du if if bool_expr [then] some_code elsif bool_expr [then] some_code else some_code end # if est une expression dont la valeur est le résultat de la derniere # instruction ma_valeur = if bool_expr [then] some_code elsif bool_expr [then] some_code else some_code end # Un test peut aussi s'écrire : some_code if bool_expr # Remarque "if not" peut etre remplacé par "unless" some_code unless bool_expr # enfin, vous pouvez utiliser les expessions ternaires comme en C/C++ : ma_valeur = bool_expr ? "result_si_oui" : "result_si_non"
Instruction case (le switch)
Utilisation classique :case inputLine when "debug" # détection d'un chaine précise dumpDebugInfo dumpSymbols when /p\s+(\w+)/ # détection et exploitation d'une expression régulière dumpVariable($1) when "quit", "exit" # plusieurs mots possibles exit else print "Illegal command: #{inputLine}" endIl faut bien comprendre que ce "case" est beaucoup plus puissant que le "switch" de C. En effet, le travail de comparaison effectué par la clause "when" dépend (mais de manière naturelle) à la fois du type de la variable inputLine et du type de ce qui suit le "when".
les boucles
while bool_expr some_code end Comme pour le if, on peut mettre le while après l'instruction some_code while bool_expr et si some_code prend plusieurs lignes, il faut délimité par begin..end d'où la forme utilisée dans le TP : begin some_code end while bool_expr
Un exemple de classe complète
A FAIREEn attendant : voir wikipedia/ruby
Introspection
Tout est objet en Ruby ? Alors pouquoi il existe par exemple des fonctions comme puts, print, ... Et bien parce que ce sont en fait des méthodes d'un objet implicite de classe Kernel.13.class => Fixnum 13.0.class => FloatC'est d'ailleurs pour cela que 13. (avec un point) ne peut pas représenter un flottant, comme en C. En effet le point suivant le 13 permet d'appeler une méthode de l'objet de classe Fixnum.
"my text".class => String [ 1,2,3 ].class => Array { 1 => "Ain", 2 => "Aisne", 3 => "Allier" }.class => HashLes classe ruby sont elles-même des objets d'une certaine classe :
String.class => Class String.ancestors => [String, Enumerable, Comparable, Object, Kernel] Class.ancestors => [Class, Module, Object, Kernel] 1.class.ancestors => [Fixnum, Integer, Precision, Numeric, Comparable, Object, Kernel]Au fait, 2 est un objet ! De quels méthodes dispose-il ?
2.methods
["%", "odd?", "inspect", "prec_i", "<<", "tap", "div", "&", "clone", ">>", "public_methods", "object_id", "__send__", "instance_variable_defined?", "equal?", "freeze", "to_sym", "*", "ord", "+", "extend", "next", "send", "round", "methods", "prec_f", "-", "even?", "singleton_method_added", "divmod", "hash", "/", "integer?", "downto", "dup", "to_enum", "instance_variables", "|", "eql?", "size", "instance_eval", "truncate", "~", "id", "to_i", "singleton_methods", "modulo", "taint", "zero?", "times", "instance_variable_get", "frozen?", "enum_for", "display", "instance_of?", "^", "method", "to_a", "+@", "-@", "quo", "instance_exec", "type", "**", "upto", "to_f", "<", "step", "protected_methods", "<=>", "between?", "==", "remainder", ">", "===", "to_int", "nonzero?", "pred", "instance_variable_set", "coerce", "respond_to?", "kind_of?", "floor", "succ", ">=", "prec", "to_s", "<=", "fdiv", "class", "private_methods", "=~", "tainted?", "__id__", "abs", "untaint", "nil?", "chr", "id2name", "is_a?", "ceil", "[]"]
Cela fait beaucoup de méthodes hein ! Je voudrais afficher seulement
l'ensemble des méthodes de l'objet 2 sans les méthodes communes
à tout objet, triées, et avec seulement une virgule-espace comme séparateur ??
(2.methods - Object.methods).sort.join(", ")
=> "%, &, *, **, +, +@, -, -@, /, <<, <<, [], ^, abs, between?, ceil, chr, coerce, div, divmod, downto, even?, fdiv, floor, id2name, integer?, modulo, next, nonzero?, odd?, ord, prec, prec_f, prec_i, pred, quo, remainder, round, singleton_method_added, size, step, succ, times, to_f, to_i, to_int, to_sym, truncate, upto, zero?, |, ~"
Exercice : faites la même chose avec C++, java ou python...
De même, quelles sont les méthodes disponibles pour un objet tableau ?
([1, 2, 3].methods - Object.methods).sort.join(", ")
=> "&, *, +, -, <<, [], []=, all?, any?, assoc, at, choice, clear, collect, collect!, combination, compact, compact!, concat, count, cycle, delete, delete_at, delete_if, detect, drop, drop_while, each, each_cons, each_index, each_slice, each_with_index, empty?, entries, enum_cons, enum_slice, enum_with_index, fetch, fill, find, find_all, find_index, first, flatten, flatten!, grep, group_by, index, indexes, indices, inject, insert, join, last, length, map, map!, max, max_by, member?, min, min_by, minmax, minmax_by, nitems, none?, one?, pack, partition, permutation, pop, product, push, rassoc, reduce, reject, reject!, replace, reverse, reverse!, reverse_each, rindex, select, shift, shuffle, shuffle!, size, slice, slice!, sort, sort!, sort_by, take, take_while, to_ary, transpose, uniq, uniq!, unshift, values_at, zip, |"
Ajouter que la classe String est également un objet constant de la class "class", ...
Bric à Brac
Fibonacci en Ruby :Principe : en créer une Hash (dictionnaire) mémorisant les valeurs de la fonction de Fibonacci. Le bloc {...} permet de calculer la valeur intiale pour une clé donnée si celle-ci n'a jamais été initialisée. L'appel à merge permet d'ajouter à cette Hash les deux premiers éléments de la suite de Fibonacci.
irb fibs = Hash.new {|h,k| h[k] = h[k-1] + h[k-2]}.merge(0=>0, 1=>1) fibs[70] => 190392490709135Passage little endian en big endian d'une chaine hexa sur mot de 32 bits :
Algo
- découper la chaine pour obtenir un tableau de blocs de 8 caractères
- découper chaque bloc en sous-tableau de chaines de deux caractères (i.e. un byte)
- inverser l'ordre de ce sous-tableau
- réunir ces sous-tableau pour en faire des sous-chaine (inversées)
- réunir le tableau final
irb s = "1234567812345678" s.scan(/.{8}/).map{|b| b.scan(/.{2}/).reverse.join}.join =>> "7856341278563412" # Variante s.scan(/(..)(..)(..)(..)/).map(&:reverse).joinAutre solution : utiliser les flux potentiellement de taille infinie de ruby-2.0 (avec lazy...) TODO
Une chaine aléatoire en ruby :
(1..50).map{ ('a'..'z').to_a.sample }.join # => "maeutvhotoracijglbcvlgavclfbkppceubwyebbqufthmuyyd" # Vous voulez une ligne de commande sous unix ruby -e 'p (1..50).map{%w(0 1 O I).sample}.join' # =>"10O00110101I10IIOI10OIIII0OI1I1OII0OOO01OO001111O0" # vous voulez maitriser les caractères utilisés ? chars = (0..9).to_a + ('A'..'Z').to_a + ('a'..'z').to_a + %w(! @ # $ % * . ,); passwd = (1..50).map{chars.sample}.join # => "w%jz0@cpf4.WpXbkpATbk!Zyhij8lerRNzsL0L93Jv5.$CTJEa"A SUIVRE
./