[PYTHON] Générateur automatique d'entrée compétitif Pro et histoire orientée objet

Aperçu

<! - * Génère également automatiquement le code source (Python) pour recevoir l'entrée->

Ceux qui veulent utiliser la fonction de génération automatique, ainsi que ceux qui veulent en savoir plus sur la programmation qui va un peu plus loin du pro de la concurrence Je pense que ce contenu peut être lu par ceux qui étudient l'orientation objet. Le code source complet peut être trouvé à [ici] 1.

Ce que vous pouvez faire: générer automatiquement des cas de test d'entrée

Si vous préparez une telle macro d'entrée →

Int(N,2,5)
Float(a,1,100,5) Perm(1,N+4)
Str([a-z]{3,2*N})
*N(2)

L'entrée sortira →

output: 
4
54.81887 3 2 4 7 1 8 5 6
yyl
4.32497 4 1 6 5 3 8 7 2
yziuqiac
42.84603 3 2 4 7 8 6 5 1
vsjajs
65.07176 7 5 8 3 4 6 1 2
rbq

Diverses autres choses

Graphique pondéré:

Int(N,3,5) Int(M,N-1,N*(N-1)//2)
Graph1(N,M,0,1000)

output:
4 5
1 2 392
1 3 328
1 4 891
2 3 264
3 4 227

Chaîne:

Int(N,4,6) Int(M,4,6)
Str([.#]{M})
*N(1)

output:
4 5
###.#
#.#.#
#..##
.###.

etc.

Si vous êtes intéressé par la fonction elle-même, vous pouvez trouver une liste dans [ici] 1.

<! - ## Ce que vous pouvez faire: générer automatiquement le code source pour recevoir l'entrée

Au lieu de cracher des entrées, vous pouvez également utiliser la fonction input () de Python pour générer du code source qui reçoit des entrées:


``` -->

 * Puisque nous n'avons défini que la classe interne jusqu'à présent, si vous souhaitez générer un grand nombre de cas de test à la fois ou l'écrire dans un fichier, vous devez coder Python en utilisant cette classe.

## Méthode de réalisation

 Il a une implémentation orientée objet.
 Grossièrement

 1. Classe responsable du chargement de la macro entière (LineCollection)
 1. Classe (Ligne) responsable de la lecture de la macro ligne par ligne
 1. La classe (Item et ses sous-classes) qui est responsable du délimiteur d'espace sur une ligne de la macro

 Il est divisé en. C'est une image que je gère i + 1 et jette le travail nécessaire (i = 1,2).
 Toutes les classes ont les méthodes suivantes en commun:

 1. from_str (): Analyse la chaîne de macro d'entrée et initialise la classe.

 1. generate (): génère un cas de test d'entrée aléatoire pour la plage qu'il gère.

 En résumé, cela ressemble au tableau ci-dessous:

 ||LineCollection|Line|Item|
 |---|---|---|---|
 |from_str()|Chargement de la macro entière|Lire une ligne|Lire un mot|
 |generate()|Génération de sortie pour toute la macro|Génération de sortie pour une ligne|Génération de sortie pour un mot|

 <!-- |generate()/generate_source()|Génération de sortie pour toute la macro|Génération de sortie pour une ligne|Génération de sortie pour un mot| -->


 Le point est

 * Pour utiliser l'ensemble, LineCollection.from_str (macro d'entrée) → generate ()

 <! - * Pour utiliser le tout, LineCollection.from_str (macro d'entrée) → generate () / generate_source () ->

 * Si vous souhaitez augmenter le nombre de nouveaux types de macros, définissez simplement une sous-classe de la classe Item qui a les trois méthodes ci-dessus (pas besoin de changer Line ou LineCollection).

 Quel endroit, comme.

## Code source

 Plus précisément, nous vérifierons le code source.

 LineCollection

 ||<font color=green> LineCollection|Line|Item|
 |---|---|---|---|
 |from_str()|<font color=green>Chargement de la macro entière</font>|Lire une ligne|Lire un mot|
 |generate()|<font color=green>Génération de sortie pour toute la macro|Génération de sortie pour une ligne|Génération de sortie pour un mot|

 <!-- |generate()/generate_source()|<font color=green>Génération de sortie pour toute la macro|Génération de sortie pour une ligne|Génération de sortie pour un mot| -->


 self.ls a une liste de lignes pour chaque ligne de la macro.
 C'est un peu compliqué de supporter des macros comme "* N (1)", mais ce que nous faisons est

 * from_str () = Charger toute la macro: Initialisez la ligne correspondant à chaque ligne et mettez-la dans self.ls
 * generate () = Générer une sortie pour toute la macro: Générer une sortie pour chaque ligne dans self.ls et la renvoyer avec un caractère de saut de ligne

 seulement.

```python
class LineCollection:
    def __init__(self, ls, s=None):
        """ls: list of Line
        """
        self.ls = ls
        self.s = s
    @classmethod
    def from_str(cls, s):
        lines = s.split("\n")
        i = 0
        ls = []
        for i in range(len(lines)):
            if lines[i].startswith("*"):
                name, num = lines[i][1:].split("(",1)
                num = int(num[:-1])
                ls.append((name, num))
            else:
                l = Line.from_str(lines[i])
                ls.append(l)
        return cls(ls, s)
    def generate(self):
        i = 0
        prv = 0
        output = []
        while i<len(self.ls):
            while i<len(self.ls) and not isinstance(self.ls[i], tuple):
                i += 1
            if i<len(self.ls) and isinstance(self.ls[i], tuple):
                m, num = self.ls[i]
                i += 1
            else:
                m = 0
                num = 0
            for j in range(prv, i-num-1):
                if isinstance(self.ls[j], tuple):
                    continue
                output.append(self.ls[j].generate())
            if num!=0:
                try:
                    m = Item.names[m]
                except KeyError:
                    raise ValueError("Nombre indéfini de cas de test:La spécification du nombre de lignes à voir ci-dessus n'est-elle pas fausse?")
                for _ in range(m):
                    for j in range(i-num-1, i-1):
                        if isinstance(self.ls[j], tuple):
                            continue
                        output.append(self.ls[j].generate())
            prv = i
        return "\n".join(output)

Line

LineCollection Line Item
from_str() Chargement de la macro entière Lire une ligne Lire un mot
generate() Génération de sortie pour toute la macro Génération de sortie pour une ligne Génération de sortie pour un mot

self.l gère une liste d'éléments qui existent dans la ligne que vous regardez. Similaire à LineCollection,

Je fais ça. Là où nous lisons le nom de la classe et initialisons l'élément, nous obtenons l'objet de classe à partir de la fonction intégrée Python globals ().

def evaluate_item(ss):
    cc, tmp = ss.split("(", 1)
    vv = tmp[:-1]
    return globals()[cc].from_str(vv)

class Line:
    def __init__(self, l, s=None):
        """
        correspond to a line of input file
        l: list of Item
        """
        self.l = l
        self.s = s
    @classmethod
    def from_str(cls, s):
        l = []
        for ss in s.split():
            l.append(evaluate_item(ss))
        return cls(l)
    def generate(self):
        return " ".join([item.generate() for item in self.l])

Item

LineCollection Line Item
from_str() Chargement de la macro entière Lire une ligne Lire un mot
generate() Génération de sortie pour toute la macro Génération de sortie pour une ligne Génération de sortie pour un mot

Enfin, Item, qui est défini pour chaque macro que vous souhaitez prendre en charge. Dans un tel cas, créer une classe de base et en hériter est supérieur en termes de lisibilité, maintenabilité et efficacité de codage. Nous avons préparé la classe Item comme classe de base comme suit:

class Item:
    names = {}
    def __init__(self, s=None, **keys):
        self.s = s
    @classmethod
    def from_str(cls, s):
        pass
    def evaluate(self, s):
        for k in Item.names.keys():
            if k in s:
                s = s.replace(k, str(Item.names[k]))
        return eval(s)
    def generate(self):
        pass
    def __str__(self):
        if hasattr(self, "s"):
            return self.s

L'objet est une classe qui ne peut pas être utilisée telle quelle, il est donc préférable de mettre un sort magique pour le représenter, mais cette fois, il est omis. Le simple fait de déclarer que vous disposez de ces méthodes présente de grands avantages, par exemple en facilitant la compréhension des autres lors de l'ajout de fonctionnalités.

En dessous, créez une classe pour chaque macro que vous souhaitez réaliser. Par exemple, Int

class Int(Item):
    def __init__(self, name, low, high, s=None, **keys):
        """
        correspond to the input value between two spaces
        name: str
            name of variable
        low/high : str
            min / max (inclusive)
        """
        self.name = name
        self.low = low
        self.high = high
        self.keys = keys
        Item.__init__(self, s)
    @classmethod
    def from_str(cls, s):
        name, low, high = s.split(",")
        return cls(name, low, high, s=s)
    def generate(self):
        low, high = self.evaluate(self.low), self.evaluate(self.high)
        value = utils.rng.integers(low, high+1)
        Item.names[self.name] = value
        return str(value)

Si l'ordre est 1 ... N

class Perm(Item):
    """permutation of [low, low+1, ..., high-1, high]
    """
    def __init__(self, low, high, s=None):
        self.low = low
        self.high = high
        self.s = s
    @classmethod
    def from_str(cls, s):
        low, high = s.split(",")
        return cls(low, high, s=s)
    def generate(self):
        low, high = self.evaluate(self.low), self.evaluate(self.high)
        return " ".join(map(str, (utils.rng.permutation(high-low+1) + low).tolist()))

Il a la forme. Lorsque des nombres aléatoires sont nécessaires, ils sont jetés dans un randomiseur défini en externe:

utils.py


import numpy as np
SEED = 0
rng = np.random.default_rng(SEED)

Lors de l'utilisation de nombres aléatoires, il est correct de ne pas appeler la fonction à chaque fois, mais de préparer des correcteurs d'aléa (multiples si nécessaire) et de les utiliser correctement en fonction de la portée. Cependant, dans ce cas, cela dépend s'il est souhaitable de fixer le nombre aléatoire (pour obtenir la reproductibilité) ou de changer le résultat pour chaque exécution, il peut donc être nécessaire de le réécrire en fonction du cas d'utilisation. ..

Les autres macros seront prises en charge une par une de la même manière. Par exemple, s'il s'agit d'un graphe, il est réalisé à l'aide de fonctions telles que la génération de graphes aléatoires networkx, et s'il s'agit d'une chaîne de caractères, il est réalisé en utilisant des fonctions telles que la génération aléatoire d'une chaîne de caractères qui correspond au modèle d'expression régulière de rstr. Que faire de toute façon

C'est juste une question de mise en œuvre, donc l'effort pour ajouter des fonctions est réduit!

Résumé

<! - * Génère également automatiquement le code source (Python) pour recevoir l'entrée->

TODO

Recommended Posts

Générateur automatique d'entrée compétitif Pro et histoire orientée objet
Compétitif Pro avec Python et VSCode-Simplification de l'entrée standard et automatisation du jugement de cas d'échantillons-