Greboca  

Suport technique et veille technologique

Suport technique et veille technologique

Aujourd’hui, les grandes entreprises et administrations publiques hésitent entre continuer à utiliser des logiciels propriétaires ou basculer vers les Logiciels Libres. Pourtant, la plupart des logiciels libres sont capables de bien traiter les données issues des logiciels propriétaire, et parfois avec une meilleur compatibilité.

C’est alors la barrière de la prise en main qui fait peur, et pourtant...

Les logiciels libres

L’aspect « Logiciel Libre » permet une évolution rapide et une plus grande participation des utilisateurs. Les aides et tutoriels foisonnent sur Internet ou sont directement inclus dans le logiciel lui-même.

Enfin, les concepteurs sont plus proches des utilisateurs, ce qui rend les logiciels libres plus agréable à utiliser et conviviaux.

Grâce à la disponibilité des logiciels libres, vous trouverez facilement des services de support techniques et la licence n’est plus un frein à l’utilisation de ces logiciels par votre personnel.

Notre support technique concerne essentiellement les logiciels libres, que ce soit sous forme de services ponctuels ou de tutoriels.

LinuxFr.org : les journaux  -  port des for_comprehension de scala en ruby

 -  4 janvier - 

Sommaire

contexte: map et flatMap

Scala est un langage fonctionnel, et donc les operateurs map et flatMap sont très utilisés.

Pour rappel:

  • map perment d'appliquer une fonction à chaque élément d'une structure de données.
  • flatMap permet d'appliquer une fonction qui retourne une structure de données à chaque élément d'une structure de données, et de "déplier" le résultat.

Cela s'applique à des liste, mais pas seulement.

exemples de map

Avec une liste:

val list = List(1, 2, 3)
val result = list.map(x => x * 2)
// result: List(2, 4, 6)

Avec un Option:

val option = Some(1)
val result = option.map(x => x * 2)
// result: Some(2)

val option = None
val result = option.map(x => x * 2)
// result: None

exemples de flatMap

Avec une liste:

val list = List(1, 2, 3)
val result = list.flatMap(x => List(x, x * 2))
// result: List(1, 2, 2, 4, 3, 6)

Avec un Either (un type qui peut contenir soit une valeur (auquel cas, c'est un Right) ou une erreur (Left)):

val either = Right(1)
val result = either.flatMap(x => Right(x * 2))
// result: Right(2)

val either = Left("error")
val result = either.flatMap(x => Right(x * 2))
// result: Left("error")

contexte: programmes fonctionnels

En scala, on chaine souvent map, flatMap et filter pour écrire des programmes fonctionnels.

Par exemple, supposons que l'on veuille calculer tous les tuples de 0 à 9 dont la somme est égale à 10:

val result = (0 to 9).flatMap { x =>
  (0 to 9).map { y =>
    (x, y)
  }
}.filter { case (x, y) =>
  x + y == 10
}
// result: Vector((1,9), (2,8), (3,7), (4,6), (5,5), (6,4), (7,3), (8,2), (9,1))

for comprehensions

On voit que cela devient rapidement difficile à lire. Pour cela, Scala propose les for comprehensions, qui permettent d'écrire du code plus lisible.

Avec une for comprehension, le code précédent devient:

val result = for {
    x <- 0 to 9
    y <- 0 to 9 if x + y == 10
} yield (x, y)
// result: Vector((1,9), (2,8), (3,7), (4,6), (5,5), (6,4), (7,3), (8,2), (9,1))
  • <- dénote un générateur (c'est à dire une structure de données sur laquelle on va itérer)
  • if (une guarde) permet de filtrer les éléments.
  • yield permet de retourner le résultat (ici, un tuple) (en pratique, un map).

En pratique <- est un raccourci pour flatMap.

Il est aussi possible de faire des map avec l'opérateur =.
Par exemple si on veut multiplier chaque élément d'une liste par 2, et associer ces éléments à leur double, on peut écrire:

val result = for {
    x <- List(1, 2, 3)
    y = x * 2
} yield (x, y)
// result: List((1,2), (2,4), (3,6))

port en ruby

Voulant voir si il est possible d'avoir une syntaxe ruby, j'ai cherché à reproduire les for comprehensions:

Voilà la syntaxe que je propose, en reprenant les exemples précédents, le but étant de se rapprocher le plus possible de la syntaxe scala tout en évitant de trop dénaturer le ruby:

result = for_c(
    gen(:x) { (0..9) },
    gen(:y, if_c { x + y == 10 } ) { (0..9) },
) { [x, y] }
# result: [[1, 9], [2, 8], [3, 7], [4, 6], [5, 5], [6, 4], [7, 3], [8, 2], [9, 1]] 
result = for_c(
    gen(:x) { [1, 2, 3] },
    let(:y) { x * 2 }
) { [x, y] } 
# result: [[1, 2], [2, 4], [3, 6]]

En réimplémentant Either de scala, on peut aussi faire de la gestion d'erreur tout en gardant le programme principal très lisible et en évitant d'utiliser des exceptions:

def validate_username(username)
  if username.length < 8
    Either.left("Username is too short (8 characters minimum)")
  else
    Either.right(username.downcase)
  end
end

def validate_date_of_birth(dob, today)
  if dob.next_year(18) > today
    Either.left("User must be 18 years old or older")
  else
    Either.right(dob)
  end
end

def validate_user(username, dob)
  for_c(
    gen(:username) { validate_username(username) },
    gen(:dob) { validate_date_of_birth(dob, Date.today) },
  ) { User.new(username, dob) }
end

puts validate_user("Bob", Date.new(2000, 12, 1))
# Left("Username is too short (8 characters minimum)")
puts validate_user("Bob_12345", Date.new(1960, 3, 5))
# Right(Bob_12345 (1960-03-05))

implémentation de for_c

Le gros de l'implémentation ci-dessous :

# permet de rendre disponible les variables déclarées à chaque étape dans les blocs de code
def with_binding_from_hash(variables, &block)
  obj = Object.new
  variables.each do |key, value|
    obj.instance_variable_set("@#{key}", value)
    obj.define_singleton_method(key) { instance_variable_get("@#{key}") }
  end
  obj.instance_eval(&block)
end

# interprétèe les blocs de code en prenant en compte les guards
def for_comprehension_with_guard(head, env)
  with_binding_from_hash(env, &head).map do |value|
    new_env = env.clone.merge({ head.name => value })
    [ new_env, value ]
  end.select do |new_env, value|
    head.guard.nil? || with_binding_from_hash(new_env, &head.guard)
  end
end

# méthode principale
def for_comprehension(ranges, env = {}, &block)
  if ranges.empty?
    with_binding_from_hash(env, &block)
  else
    head, *tail = ranges
    # si on est à la fin de la liste ou que le prochain élément est un let, on fait un map, sinon un flat_map
    mapping = tail.empty? || tail[0].is_a?(Let) ? :map : :flat_map
    if head.is_a? Gen
      # si c'est un générateur, on itère sur les valeurs
      for_comprehension_with_guard(head, env).send(mapping) do |new_env, value|
        for_comprehension(tail, new_env, &block)
      end
    elsif head.is_a? Let
      # si c'est un let, on associe la valeur à la variable
      value = with_binding_from_hash(env, &head)
      new_env = env.clone.merge({ head.name => value })
      for_comprehension(tail, new_env, &block)
    end
  end
end

Pour les curieux, l'implémentation complète de for_c est disponible sur gist.github.com,

conclusion

Le lecteur jugera de la pertinence d'utiliser cette syntaxe ainsi que de sa lisibilité.

Le code n'est pas du tout optimisé, cela reste une expérimentation.

Je suis en train de m'amuser à implémenter des effets (IOs) à la Haskell/cats effects/ZIO en ruby,
et cette syntaxe simplifiera grandement le code.

Commentaires : voir le flux Atom ouvrir dans le navigateur

par yazgoo

LinuxFr.org : les journaux

LinuxFr.org : Journaux

Pâques, le bug d'Excel et la difficile adaptation de LibreOffice

 -  25 avril - 

Sommaire Où l'on décide de la date de Pâques Ce cher Jules 1254 ans fast forward Le calendrier julien révisé Bon, et Excel, dans tout ça ? LibreOffice (...)


Sauver des données embarquée à dos d'outarde

 -  8 avril - 

Ou comment l'association GEBULL, petit GUL de province, a contribué à une étude scientifique.À la fin du mois de novembre 2021, une balise de suivi (...)


IA : Imitation Artificielle

 -  28 mars - 

Bonjour Nal',Tout le monde cause de l'IA. L'IA par ci, l'IA par là. L'IA dans les journaux qui trouvent qu'il y a trop d'IA.Ça commence à (...)


Un super Logic Analyzer DIY pour pas cher

 -  26 mars - 

Wouah, le titre cryptique. Un peu de Wikipedia pour éclaircir (j’espère que c'est mieux que ChatGPT):L’analyseur logique est un outil de mesure (...)


Mise a jour de la traduction française du Wiki de Scribus

 -  24 mars - 

Je viens de commencer à mettre à jour la traduction française du Wiki du logiciel de Publication Assistée par Ordinateur (PAO) Scribus (équivalent (...)