• # En Python, classieux

    Posté par (site web personnel) . Évalué à 3.

    #! /usr/bin/python3
    # Advent of Code 2022, day 2
    from typing import Iterable, Optional, TypeVar
    # '@' is not a valid item type, but it does no harm, and having it makes the
    # value of each item type equal to its index in this string. :-)
    letters = "@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
    priorities = {letter: value for (value, letter) in enumerate(letters)}
    class Rucksack:
     def __init__(self, left: str, right: str):
     self.left = left
     self.right = right
     def anomaly(self) -> str:
     for letter in self.left:
     if letter in self.right:
     return letter
     raise ValueError("no anomaly in this rucksack")
     def __contains__(self, letter: str) -> bool:
     return letter in self.left or letter in self.right
    def import_sack(line):
     n = len(line) - 1 # do not count final '\n'
     left = line[0:n//2]
     right = line[n//2:n]
     return Rucksack(left, right)
    T = TypeVar('T')
    def group(iterable: Iterable[T], n: int) -> Iterable[list[T]]:
     result = [] # type: list[T]
     for item in iterable:
     result.append(item)
     if len(result) >= n:
     yield result
     result = []
     yield result
    def part1(lines: Iterable[str]) -> int:
     total = 0
     for line in lines:
     sack = import_sack(line)
     total += priorities[sack.anomaly()]
     return total
    def part2(lines: Iterable[str]) -> int:
     total = 0
     for line_group in group(lines, 3):
     sacks = [import_sack(line) for line in line_group]
     for letter in letters:
     if all(letter in sack for sack in sacks):
     total += priorities[letter]
     break
     return total
  • # Python pas classieux mais avec des set

    Posté par . Évalué à 3.

    #! /usr/bin/python3
    fichier = 'input_day_3'
    def priority(c):
     return 26*c.isupper() + (ord(c)&31)
    items_p = badges_p = grp_size = 0
    badges_set = set()
    with open(fichier,'r') as f:
     while line := f.readline().strip():
     # part 1
     l = len(line) // 2
     items_p += sum(priority(c) for c in {*line[:l]} & {*line[l:]})
     # part 2
     if not badges_set: 
     badges_set = {*line}
     else:
     badges_set &= {*line}
     grp_size += 1
     if grp_size % 3 == 0:
     badges_p += priority(badges_set.pop())
    print(items_p, badges_p)
    • [^] # Re: Python pas classieux mais avec des set

      Posté par . Évalué à 1. Dernière modification le 13 décembre 2022 à 09:48.

      # part 1
      print ("part1:", sum( ( ord(i)-38 , ord(i)-96) [ ord(i)>90 ] for x in open("input").readlines() for i in set(x[:len(x)//2]).intersection(x[len(x)//2:])))
      # part 2
      def part2():
       with open("input") as infile:
       for line1 in infile:
       line2 = next(infile)
       line3 = next(infile)
       for i in set(line1.strip()).intersection(line2).intersection(line3):
       yield ( ( ord(i)-38 , ord(i)-96) [ ord(i)>90 ])
      print("part2:", sum(list(part2())))

      (oui j'arrive après la bataille ;-) )

  • # un bout de AWK

    Posté par . Évalué à 3.

    disclamer : j'ai d'abord écrit une solution en python parce que j'ai tout de suite penser à utiliser les set. puis en AWK pour le plaisir :)

    part 1

    {
     split(substr($0,1,length($0)/2),arr,"")
     r = substr($0,1+length($0)/2)
     for (a in arr) {
     c = arr[a]
     if (index(r,c)>0) {
     comm = c
     }
     }
     S += index("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", comm)
    }
    END {print S}

    part 2

    NR%3==1 {split($0,arr,"");for(a in arr){A[arr[a]]=1}}
    NR%3==2 {split($0,arr,"");for(a in arr){B[arr[a]]=1}}
    NR%3==0 {
     split($0,arr,"")
     for (a in arr) {
     c = arr[a]
     if ((c in A)&&(c in B)) {comm = c}
     }
     S += index("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", comm)
     delete A
     delete B
    }
    END {print S}
  • # un bout de coquillage

    Posté par (site web personnel, Mastodon) . Évalué à 2.

    Quelles commande(s) allons-nous (re)découvrir aujourd'hui ?
    Voici pour la première partie.

    #!/bin/sh
    # 1ドル: input file
    for c in 'grep' 'sed' 'sort' 'test'
    do
     if ! command -v "$c" >/dev/null
     then
     echo "Error: command '$c' not found" >&2
     exit 3
     fi
    done
    if test -z "1ドル"
    then
     echo "Please call me with an input file..." >&2
     exit 1
    fi
    if grep -Eqsv '^[a-zA-Z]+$' "1ドル"
    then
     echo "Found invalid line, check the file!" >&2
     exit 2
    fi
    _if="1ドル"
    _fc=$( mktemp ) # first compartiment
    _sc=$( mktemp ) # second compartiment
    _si='' # shared items
    while IFS= read -r line <&3
    do
     printf '%s' "$line" | cut -c -$(( ${#line}/2 )) |
     sed 's/./&\n/g' | sort -uo "$_fc"
     printf '%s' "$line" | cut -c $(( ${#line}/2 + 1 ))- |
     sed 's/./&\n/g' | sort -uo "$_sc"
     _si="$_si$( comm -12 "$_fc" "$_sc" )"
    done 3<"$_if"
    rm "$_fc" "$_sc"
    #echo "$_si" | cat -vet
    _ps=0 # priority sum
    _ac=0 # ASCII code
    for character in $( echo "$_si" )
    do
     _ac=$( printf '%d' "'$character" )
     case "$character" in
     [a-z]) # a is 97 but should translate to 1
     _ps=$(( _ps + _ac - 96 )) ;;
     [A-Z]) # A is 65 but should translate to 27
     _ps=$(( _ps + _ac - 38 )) ;;
     *) echo "$character=$_ac=??" >&2 ;;
     esac
    done
    echo "$_ps"

    La grande difficulté est que l'écriture suivante (à laquelle j'ai initialement pensée) n'est pas POSIX (c'est une facilité de (ba|k|z)sh mais pas reconnue dans ash par exemple)

    _fc='' # first compartiment
    _sc='' # second compartiment
    _si='' # shared items
    while IFS= read -r line
    do
     _fc="$( printf '%s' "$line" | cut -c -$(( ${#line}/2 )) |
     sed 's/./&\n/g' | sort )"
     _sc="$( printf '%s' "$line" | cut -c $(( ${#line}/2 + 1 ))- |
     sed 's/./&\n/g' | sort )"
     _si="$_si$( comm -12 <( printf '%s' "$_fc" ) <( printf '%s' "$_sc" ) )"
    done <"$_if"

    Pas grave, on troque l'utilisation de RAM (dont la machine où j'ai testé manque un peu) contre des fichiers (j'ai des E/S sur disque assez performantes pour que ce ne soit pas problématique.)

    "It is seldom that liberty of any kind is lost all at once." ― David Hume

    • [^] # Re: un bout de coquillage

      Posté par (site web personnel, Mastodon) . Évalué à 2. Dernière modification le 12 décembre 2022 à 01:47.

      La seconde partie m'a fait pensé à split pour les groupes de trois... Mais bon, ça fait pléthore de fichiers à gérer ensuite, et puis l'adaptation de la solution précédente n'est pas vraiment difficile (le truc en sus est d'arriver à compter ...modulo trois.)

      #!/bin/sh
      # 1ドル: input file
      for c in 'cat' 'fold' 'grep' 'sort' 'test'
      do
       if ! command -v "$c" >/dev/null
       then
       echo "Error: command '$c' not found" >&2
       exit 3
       fi
      done
      if test -z "1ドル"
      then
       echo "Please call me with an input file..." >&2
       exit 1
      fi
      if grep -Eqsv '^[A-Za-z]+$' "1ドル"
      then
       echo "Found invalid line, check the file!" >&2
       exit 2
      fi
      _if="1ドル"
      if test $(( $( cat "$_if" | wc -l ) % 3 )) -ne 0
      then
       echo "Number of lines not multiple of 3" >&2
       exit 2
      fi
      _fc=$( mktemp ) # becomes first rucksack
      _sc=$( mktemp ) # becomes second rucksack
      _tc=$( mktemp ) # third rucksack
      _si='' # shared items
      _gc=0 # group counter
      _bl='' # badges list
      while IFS= read -r line <&3
      do
       _gc=$(( _gc + 1 ))
       case $(( _gc % 3 )) in
       1) # 1st member of group
       printf '%s' "$line" | fold -w 1 | sort -uo "$_fc"
       ;;
       2) # 2nd member of group
       printf '%s' "$line" | fold -w 1 | sort -uo "$_sc"
       _si="$( comm -12 "$_fc" "$_sc" )"
       ;;
       0) # 3rd member of group
       printf '%s' "$line" | fold -w 1 | sort -uo "$_tc"
       _bl="$_bl$( printf '%s' "$_si" | comm -12 "$_tc" - )"
       ;;
       *) echo "?!?$_gc!?!" >&2 ;;
       esac
      done 3<"$_if"
      rm "$_fc" "$_sc" "$_tc"
      #echo "$_bl" | cat -vet
      _ps=0 # priority sum
      _ac=0 # ASCII code
      for character in $( echo "$_bl" )
      do
       _ac=$( printf '%d' "'$character" )
       case "$character" in
       [a-z]) # a is 97 but should translate to 1
       _ps=$(( _ps + _ac - 96 )) ;;
       [A-Z]) # A is 65 but should translate to 27
       _ps=$(( _ps + _ac - 38 )) ;;
       *) echo "$character=$_ac=??" >&2 ;;
       esac
      done
      echo "$_ps"

      Le vice avait été poussé jusqu'à éviter grep -o car cette option n'est pas POSIX (bien que je la trouve un peu partout ceci dit.) Ici, je propose une autre approche que j'avais mentionnée dans l'un de mes journaux. En terme de perfs, faut voir.

      $ time echo "split" | sed 's/./&\n/g'
      s
      p
      l
      i
      t
      real 0m0.014s
      user 0m0.002s
      sys 0m0.004s
      $ time echo "split" | grep -o '.'
      s
      p
      l
      i
      t
      real 0m0.006s
      user 0m0.002s
      sys 0m0.004s
      $ time echo "split" | fold -w1
      s
      p
      l
      i
      t
      real 0m0.008s
      user 0m0.002s
      sys 0m0.007s

      Il y a certainement des optimisations possibles, mais comme on le sait faut pas s'y lancer prématurément.

      "It is seldom that liberty of any kind is lost all at once." ― David Hume

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.