Convocatoria 2020-2021
Nº de proyecto 226
Responsable del proyecto:
Marcos Bujosa Brun
Facultad de CC. Econ´omicas y Empresariales
Exámenes “online” con corrección automática: Más allá de las pruebas
tipo test de opción múltiple
1. Objetivos propuestos en la presentaci ´on del proyecto 2
2. Objetivos alcanzados 4
3. Metodolog´ıa empleada en el proyecto 5
4. Recursos humanos 6
5. Desarrollo de las actividades 6
6. Anexos 6
6.1. C ´odigo del m ´odulo prop.py de Python . . . . 6
6.2. Tres ejemplos de preguntas “programadas” con prop.py . . . . 52
6.2.1. Pregunta que solo emplea el c ´alculo proposicional . . . . 52
6.2.2. Otra pregunta que solo emplea el c ´alculo proposicional . . . . 54
6.2.3. Con c ´alculo proposicional y generaci ´on aleatoria de n ´umeros . . . . 57
6.2.4. Pregunta “HipotesisMLG-4Supuestos” . . . . 60
6.2.5. Pregunta “AjusteMCOdeMLS-mediasDeXeY” . . . . 80
6.2.6. Pregunta “ContrasteTStudent-TomaDecision” . . . 116
1. Objetivos propuestos en la presentaci ´ on del proyecto
Si anta ˜no pod´ıa resultar ´util disponer de un amplio banco de preguntas para realizar una efectiva evaluaci ´on continua, que estimulara el trabajo del estudiante, tras la pan- demia dicha disponibilidad se ha convertido en muchos casos en una necesidad. Las medidas de distanciamiento nos empujaron a emplear t ´ecnicas de evaluaci ´on online con la consiguiente dificultad de supervisi ´on. Una manera de combatir el potencial fraude son los ejercicios de evaluaci ´on individuales, pero esto supone una elevada carga de trabajo.
Consecuentemente los ejercicios individuales se han plasmado en forma de ejercicios tipo test en muchas asignaturas. Aun as´ı, una asignatura multitudinaria requiere de un banco de preguntas verdaderamente grande para poder elaborar ex ´amenes individuales;
y la elaboraci ´on, mantenimiento y gesti ´on de un banco as´ı no es en modo alguno sencilla, pero si bastante costosa. Por otra parte, la urgencia de la situaci ´on hizo que se optara mayoritariamente por la herramienta de disponibilidad m ´as inmediata para el profesorado de la Complutense: la plataforma Moodle. As´ı, el m ´etodo de trabajo fue escribir un banco de preguntas con el editor interno de Moodle, que posteriormente son barajadas para componer, por azar, ex ´amenes individuales.
Inconvenientes
que presenta este m ´etodo de trabajo.
El proceso de escritura y la edici ´on de las preguntas no son independientes.
Las alternativas de exportaci ´on de preguntas fuera de la plataforma son limitadas.
El editor de Moodle es r´ıgido y limitado (engorroso con expresiones matem ´aticas).
No permite “mecanizar” la creaci ´on de variantes de las preguntas con ayuda de
Propuestas para combatir los citados inconvenientes.
Respecto al m ´etodo de tra- bajo: nuestro proyecto separa el proceso de dise ˜no y redacci ´on de las preguntas del proceso de edici ´on; m ´as relacionado con el medio por el que se trasladan las preguntas al alumno (un test en un folio impreso, un test en Moodle importado como un fichero xml, un Notebook de Jupyter desde un servidor web en la nube, etc). Esta separaci ´on permite usar herramientas externas para elaborar preguntas (estimaciones obtenidas con progra- mas estad´ısticos y muestras variadas, c ´alculos realizados con alg ´un lenguaje num ´erico o simb ´olico, etc.). As´ı, una vez “programada” la pregunta con sus variantes, ´estas pueden ser exportadas a un documento L
ATEX (para distribuir las preguntas en papel dentro del aula), o bien a un fichero xml para ser importado en Moodle, o a un Notebook de Jupyter para ser distribuido desde un servidor, etc...
Adem ´as, la programaci ´on de variantes permite elaborar r ´apidamente un banco sufi- cientemente abundante como para generar tantos ex ´amenes individuales como sea ne- cesario y, no obstante, mantener una composici ´on y dificultad homog ´enea entre todas las variantes individuales.
Este m ´etodo de trabajo facilita el mantenimiento y depuraci ´on del banco de preguntas.
Mediante una modificaci ´on del c ´odigo fuente de las preguntas es posible reelaborar el banco completo con nueva terminolog´ıa o notaci ´on en muy poco tiempo.
Todo ello permite generar y mantener bancos de preguntas suficientemente grandes como para ofrecer un servicio de ejercicios de autoevaluaci ´on “a demanda” del estu- diante: abriendo la posibilidad a un recurso educativo abierto y virtual, con una enorme potencialidad tanto en la docencia “online” o semi-presencial como en la modalidad de Flipped Classroom.
Adem ´as, el m ´etodo propuesto para la “mecanizaci ´on” en la producci ´on de variantes pretende ir m ´as all ´a de la mera variaci ´on de los “n ´umeros” de un problema dado: se pretende jugar con la “l ´ogica” interna de los enunciados y las cuestiones para generar variantes con ayuda del c ´alculo proposicional. Esto permite aumentar el n ´umero de va- riantes de manera exponencial en cuestiones no necesariamente num ´ericas.
En cuanto a los inconvenientes relacionados con la calidad de la evaluaci ´on. Preten- demos que las pruebas contengan preguntas abiertas pero sin perder la posibilidad de la correcci ´on autom ´atica. As´ı, se pueden incluir preguntas tales como “escriba una base del siguiente subespacio vectorial...”, cuya respuesta requiere escribir una lista de vectores que cumplen ciertos requisitos. Como en general existen infinitas listas que los cumplen, es necesario que el sistema pueda verificar si cada respuesta particular es correcta o in- correcta. La ´unica manera de formular una pregunta similar en un examen tipo test ser´ıa mostrar listas de vectores y solicitar que el alumno marque aquellas listas que son base del subespacio del enunciado... pero el hecho de que las listas ya est ´en “escritas frente al alumno” hace que naturaleza de la pregunta cambie (ya no necesita “encontrar”... tan solo “verificar” qu ´e opciones son correctas). Esto limita el tipo de preguntas, lo que puede dar lugar a que las pruebas reflejen de manera m ´as pobre los conocimientos del alumno.
Aunque Moodle puede distribuir ex ´amenes de tipo test en l´ınea, no dispone de un “par-
ser” que permita evaluar la correcci ´on de respuestas en forma de expresiones tales como
As´ı pues, aunque ya hemos programado en Python un int ´erprete para ser usado en ´ Alge- bra Lineal, no hemos desarrollado la parte correspondiente a los Notebooks distribuidos desde un servidor en la nube y la correspondiente gesti ´on de calificaciones.
2. Objetivos alcanzados
Respecto al desarrollo de un parser para programar preguntas abiertas
Para la asignatura de Matem ´aticas II ( ´ Algebra Lineal) se ha programado el m ´odulo para Python
NAcAL(https://pypi.org/project/nacal/); que actualmente se encuentra en un es- tado de desarrollo muy avanzado: no solo permite ser empleado como “parser”, sino que adem ´as permite traducir muchas expresiones a c ´odigo L
ATEX (as´ı, es posible usar el m ´odulo para escribir en L
ATEX no solo los objetos tales como vectores, matrices, ba- ses, ecuaciones cartesianas o param ´etricas de subespacios o de espacios afines, etc., sino tambi ´en el desarrollo completo de la resoluci ´on de algunos problemas habituales).
Adem ´as, permite el uso de variables simb ´olicas con el m ´odulo
SymPy. Aunque hemosrenunciado a intentar servir ejercicios en forma de Notebooks de Jupyter con la extensi ´on
“nbgrader” (pues no disponemos de un servidor virtual con el que intentar hacer prue- bas), el m ´odulo
NAcALya est ´a pensado para ser usado con los Notebooks de Jupyter.
Como, sin servidores donde hacer pruebas, no pod´ıamos poner en marcha esta parte del proyecto, tampoco hemos ensayado con otros m ´odulos existentes en Python para su uso en An ´alisis Matem ´atico y Optimizaci ´on, Estad´ıstica y Econometr´ıa, o Teor´ıa de Juegos.
Respecto al dise ˜no de preguntas de opci ´on m ´ultiple
Como se ha subrayado m ´as arriba, nuestro objetivo respecto a la “mecanizaci ´on” en la producci ´on de preguntas pre- tende jugar con la “l ´ogica” interna de los problemas, en lugar de limitarse a la mera al- teraci ´on de los “n ´umeros” de un problema. Por ello requer´ıamos del desarrollo de un m ´odulo de c ´alculo proposicional. As´ı pues, se ha programado un m ´odulo para Python que implementa dicho c ´alculo. Ello nos ha permitido dotar de estructura l ´ogica tanto a los enunciados como a las cuestiones.
Cada enunciado alternativo viene acompa ˜nado de una sem ´antica en forma de pro- posiciones l ´ogicas, que constituyen los supuestos del enunciado. Adem ´as, cada enunciado tiene definidas ciertas precondiciones que deben ser comprobadas para que sea incluido como variante de un problema.
De manera similar, cada cuesti ´on viene acompa ˜nada de una sem ´antica en forma de proposiciones, que tratar ´an de ser refutadas a partir de las premisas (los supuestos del enunciado correspondiente a la variante en cuesti ´on). Adem ´as, cada enunciado tiene definidas ciertas precondiciones que deben ser comprobadas para que sea incluido como variante de un problema (por ejemplo, si la cuesti ´on no es derivable a partir del enunciado, dicha cuesti ´on no ser ´a incluida como variante del problema).
As´ı, la estructura de una pregunta consiste en una una lista de enunciados y varias lis-
tas de cuestiones. Con estas listas se formar ´an por pura combinatoria todas las posibles
variantes que tengan sentido, es decir, en las que, dado el enunciado, se pueda determi-
nar la veracidad o falsedad de cada una de las cuestiones efectivamente formuladas. Es
Adem ´as, como la programaci ´on de cada pregunta se realiza con Python, la flexibilidad es enorme
1: es posible usar simulaciones, bases de datos, estimaciones, gr ´aficos, web scraping, etc, para elaborar cuantas variantes num ´ericas, gr ´aficas, etc. sean necesarias.
Tambi ´en es posible incluir el c ´odigo L
ATEX que sea necesario en las cadenas de caracteres que forman los textos de los enunciados y las cuestiones.
As´ı pues, hemos logrado separar la elaboraci ´on del banco de preguntas respecto de su edici ´on concreta (que depende del canal de distribuci ´on del examen).
Respecto a la edici ´on de las preguntas.
Una vez “programadas” las preguntas (y generadas sus m ´ultiples variantes), es necesario exportar el banco de preguntas a un formato que permita distribuir las mismas entre los alumnos. Hemos trabajado con dos formatos:
Cuestionarios en papel para cumplimentar en el aula de manera presencial
Po- demos convertir cada problema (es decir, del conjunto de variantes con sentido) en un fichero L
ATEX para ser usado como parte de un banco de preguntas
Auto Multiple Choi- ce (AMC). Los cuestionarios pueden ser nominales, o bien cada alumno puede codificarsu identidad (por ejemplo con su DNI). Estos cuestionarios son corregidos mediante una imagen escaneada de los mismos (v ´ease
https://www.auto-multiple-choice.net/).Cuestionarios en xml para ser distribuidos en l´ınea con Moodle
Otra alternativa es generar un fichero xml para ser importado por Moodle. Tiene la ventaja de que permite evaluar online, pero presenta limitaciones que no existen en los cuestionarios AMC. Por ejemplo, Moodle no permite cargar paquetes de L
ATEX, lo cual limita enormemente la nota- ci ´on empleada. Otra limitaci ´on importante aparece con las pruebas de elecci ´on m ´ultiple, pues en Moodle es imposible crear una prueba de ese tipo cuya regla de puntuaci ´on sea de esperanza nula, ya que no permite que ninguna pregunta punt ´ue negativamente.
Como hemos explicado m ´as arriba, no hemos desarrollado la exportaci ´on al forma- to de Notebook de Jupyter (formato que nos permitir´ıa formular preguntas abiertas con correcci ´on autom ´atica y distribuci ´on online).
3. Metodolog´ıa empleada en el proyecto
Hemos programado un m ´odulo para Python que implementa el c ´alculo proposicional y que nos permite generar variantes con sentido de problemas en formato test y de opci ´on m ´ultiple. Este m ´odulo permite exportar los bancos de preguntas en dos formatos distintos:
Para generar cuestionarios individuales con el programa
Auto Multiple Choice (AMC)distribuidos en papel dentro del aula, es posible exportar las preguntas a ficheros de L
ATEX con el formato AMC.
Para distribuir las preguntas con la plataforma Moodle, es posible generar ficheros
xml que se pueden importar al campus virtual de manera sencilla. La creaci ´on de
remos alg ´un ejemplo), as´ı como un fichero xml para ser directamente importado en Moodle.
Tambi ´en hemos programado un m ´odulo para Python que sirve de int ´erprete de expresio- nes para la asignatura de Matem ´aticas II ( ´ Algebra Lineal).
El c ´odigo lo hemos desarrollado usando
noweb, para escribir el c ´odigo y su documen-taci ´on en paralelo.
4. Recursos humanos
En el desarrollo de este proyecto hemos colaborado (por orden alfab ´etico): Francisco Alvarez, Marcos Bujosa, Alfredo Garc´ıa-Hiernaux, Haydee Lugo, Mar´ıa Eugenia Mera y ´ Rodrigo Mulero.
5. Desarrollo de las actividades
Hemos seguido el plan de trabajo descrito en la memoria inicial del proyecto:
1. Hemos desarollado un m ´odulo de Python para “programar” preguntas con m ´ulti- ples variantes, as´ı como para convertirlas en formatos espec´ıficos (.tex y .xml). El desarrollo de este modulo ha sido complicado: la implementaci ´on del c ´alculo pro- posicional nos ha resultado inicialmente compleja; y la generaci ´on de los ficheros xml para ser importados en Moodle dificultosa. El lenguaje xml era desconocido para nosotros, as´ı que nos hemos encontrado con problemas de codificaci ´on de caracteres, codificaci ´on de expresiones en L
ATEX, problemas en la estructura de los ficheros admitidos por Moodle, etc. Afortunadamente descubrimos una v´ıa in- directa de generar los ficheros xml a partir de ficheros L
ATEX y el paquete
moodle(https://framagit.org/mattgk/moodle). Aun as´ı, encontramos algunas dificulta- des con la codificaci ´on de algunos s´ımbolos comunes en espa ˜nol como son la aper- tura de interrogaciones o exclamaciones, pero que solventamos gracias a la ayuda del desarrollador del paquete de L
ATEX
moodle. Consideramos que las posibilidadesde este m ´odulo son espectaculares para crear grandes bancos de preguntas.
2. Tambi ´en hemos completado el m ´odulo
NAcAL(https://pypi.org/project/nacal/) para Python; que ahora permite escribir muchas expresiones y desarrollos en L
ATEX.
Esto, combinado con el paquete de L
ATEX
pythontexfacilita enormemente la redac- ci ´on de preguntas y ejercicios de ´ Algebra Lineal.
3. Con el desarollo de este software hemos empezado a generar distintos bancos de preguntas. En el anexo mostramos el c ´odigo de tres preguntas seguidos de las variantes que se generan con dicho c ´odigo. Creemos que son suficientes para dar a entender todo lo logrado en este proyecto.
6. Anexos
June 11, 2021
´Indice
1 Aparato l´ogico 2
1.1 Estructura de la librer´ıa prop.py . . . . 2
1.2 Clase Proposicion y su subclase v (proposici´on at´omica) . . . . 3
1.2.1 La subclase v para proposiciones at´omicas (i.e., para variables proposicionales) . . . . 5
1.3 Valoraci´on de proposiciones . . . . 5
1.3.1 Funci´on span . . . . 6
1.4 Funci´on extension que extiende una funci´on parcial . . . . 7
1.4.1 Implementaci´on de la funci´on extension . . . . 7
1.4.2 Reducci´on de proposiciones NO at´omicas . . . . 8
1.4.3 Proposici´on no derivable de un conjunto de premisas . . . . 11
1.4.4 ... cuando llegamos a una proposici´on at´omica (o variable proposicional ). . . . 11
1.5 Funci´on que refuta una proposici´on a partir de un conjunto de premisas . . . . 12
1.6 Funci´on test . . . . 12
2 Creaci´on de preguntas de opci´on m´ultiple 14 2.1 Estructura del aparato auxiliar de la librer´ıa prop.py . . . . 14
2.2 Clase Marcador. . . . 14
2.3 Las clases Supuesto, Cuestion y ProblemaTipo . . . . 16
2.3.1 Clase Supuesto(enunciado, semantica, precond) . . . . 16
2.3.2 Clase Cuestion(enunciado, semantica, precond) . . . . 16
2.3.3 Clase ProblemaTipo . . . . 17
2.3.4 Ejemplo de uso . . . . 20
2.4 Clase ProblemaVF . . . . 21
2.4.1 Ejemplo de uso . . . . 21
3 Dando formato a las preguntas 23 3.1 Estructura de la librer´ıa FormatoPreguntas.py. . . . 23
3.2 Preguntas en LATEX para usar conAuto-Multiple-Choice . . . . 24
3.2.1 Ejemplo de uso . . . . 26
3.3 Preguntas en xml para usar con Moodle . . . . 29
3.3.1 QuizMoodle, QuizMoodleLastCh, QuizVFMoodle y QuizVFMoodleLastCh. . . . 30
3.3.2 MoodleMultiy MoodleMultiLastCh . . . . 32
3.3.3 Ejemplo de uso de QuizMoodleLastCh y QuizVFMoodleLastCh . . . . 34
3.4 Preguntas en Notebooks de Jupyter para usar connbgrader . . . . 36
4 Ejemplo de pregunta de opci´on m´ultiple para Econometr´ıa 40 A Trozos de c´odigo 43 A.1 ´Indice de objetos y procedimientos . . . . 44
APARATO L ´OGICO
1.1 Estructura de la librer´ıa prop.py
2a hprop.py2ai≡
# coding=utf8
hAparato l´ogico de la librer´ıa2bi
hAparato auxiliar para la creaci´on de problemas de opci´on m´ultiple14i Root chunk (not used in this document).
2b hAparato l´ogico de la librer´ıa2bi≡
hDefinici´on de la clase Proposicion3ai hDefinici´on de la subclase v5i
hDefinici´on de alguno y unoDe4ai hDefinici´on de noDerivable4bi hDefinici´on de la funci´on span6ai hDefinici´on de la funci´on extension8ai hDefinici´on de la funci´on refuta12bi hDefinici´on de la funci´on test13ai This code is used in chunk2a.
ElhAparato auxiliar para la creaci´on de problemas de opci´on m´ultiple14i se desarrolla en el cap´ıtulo siguiente.
1.2 Clase Proposicion y su subclase v (proposici´ on at´ omica)
LasProposiciones son expresiones formadas con n´umeros, strings y los s´ımbolos
&, |, -, v( ), >>, **, alguno y unoDe construidas seg´un las siguientes reglas:
1. si D es un n´umero o un string entonces v(D)es una subclase deProposicionque denominaremos “variable proposicional” o “proposici´on at´omica”.
2. si P, Q y R son proposiciones tambi´en lo son P&Q, P|Q, -P, P>>Q, P**Q, alguno(P,Q,R) y unoDe(P,Q,R).
Las cinco primeras operaciones se llaman respectivamente, conjunci´on, disyunci´on, negaci´on, implicaci´on y equivalencia (si y solo si ). alguno(P,Q,R)significa que alguna de las proposiciones de la lista es verdadera yunoDe(P,Q,R)que solo una de las proposiciones de la lista es verdadera.
Ejemplo de proposici´on: “llueve o no llueve”
>>> v("llueve") | -v("llueve") v(’llueve’) | -v(’llueve’)
Necesitamos que las proposiciones del tipov(D)sean “hash-eables”, para poder ser utilizadas como claves en un diccionario. Para ello necesitamos los m´etodos hash y eq . Definamos la claseProposicion
3a hDefinici´on de la clase Proposicion3ai≡
class Proposicion:
def __init__(self,data):
self.data = data
def __hash__(self):
return hash(self.data)
def __eq__(self, another):
return hasattr(another, ’data’) and self.data == another.data hOperadores l´ogicos de la clase Proposicion3bi
hM´etodo de representaci´on de la clase Proposicion13bi This code is used in chunk2b.
Defines:
Proposicion, used in chunks3–6,10c, and11a.
Las operaciones: conjunci´on, disyunci´on, negaci´on, implicaci´on y equivalencia. Definamos como m´etodos dentro de la claseProposicionlos cinco operadores l´ogicos habituales. Primero aquellos que usan “m´etodos m´agicos”, es decir, aquellos que tiene asociado un s´ımbolo para llamar a las operaciones:
3b hOperadores l´ogicos de la clase Proposicion3bi≡
def __and__(self,other):
return Proposicion(["and", self, other]) def __or__(self,other):
return Proposicion(["or", self, other]) def __neg__(self):
return Proposicion(["not", self])
def __rshift__(self,other):
return Proposicion(["implica", self, other]) def __pow__(self,other):
return Proposicion(["equivale", self, other])
This code is used in chunk3a.
Uses Proposicion3a.
Las operaciones: unoDe y alguno. Para las dos siguientes operaciones1 no usamos ning´un “m´etodo m´agico”.
As´ı que definirlas dentro de la clase nos obligar´ıa a una inc´omoda escritura del tipo: Proposicion.alguno(P1,P2,P3) oProposicion.unoDe(P1,P2,P3). Para evitarlo, las definimos fuera de la clase; as´ı podremos escribir sencillamente alguno(P1,P2,P3)yunoDe(P1,P2,P3).
4a hDefinici´on de alguno y unoDe4ai≡
def alguno(X,*args):
return Proposicion(["alguno", X] + [a for a in args]) def unoDe(X,*args):
return Proposicion(["unoDe", X] + [a for a in args])
This code is used in chunk2b.
Defines:
alguno, used in chunks6b,10c, and11a.
unoDe, used in chunks6b,11a, and13b.
Uses Proposicion3a.
La operaci´on: noDerivable. Esta funci´on no es una operaci´on l´ogica (como si lo son las anteriores). Es un
“a˜nadido” cuya motivaci´on es la siguiente:
El esquema t´ıpico de una pregunta es: el enunciado asume que las premisas E1, E2,. . . Ek son ciertas. Y se puede puede preguntar si P es consecuencia l´ogica de las premisas. Si lo es, P es True (es decir, premisas⇒ P). Negar esto no es decir que “-P” es consecuencia de las premisas, sino que P no es derivable de las premisas (es decir, premisas6⇒ P).
As´ı que a˜nadimos la operaci´onnoDerivable. Esto nos permite, por ejemplo, que aunque para algunos enunciados se pueda preguntar si una matriz es diagonalizable, para otros evitemos la pregunta si no es deducible del enunciado.
4b hDefinici´on de noDerivable4bi≡
def noDerivable(X):
return Proposicion(["noDerivable", X])
This code is used in chunk2b.
Defines:
noDerivable, used in chunks11band13b.
Uses Proposicion3a.
1.2.1 La subclase v para proposiciones at´omicas (i.e., para variables proposicionales)
5 hDefinici´on de la subclase v5i≡
class v(Proposicion):
def __init__(self, data):
super().__init__(data)
This code is used in chunk2b.
Defines:
v, used in chunks6a,11b,20,28,30a,34b,38, and41.
Uses Proposicion3a.
1.3 Valoraci´ on de proposiciones
Una valoraci´on es una funci´on T que va del conjunto de proposiciones al conjunto {True,False} (es decir, a cada proposici´on le asigna el valor verdadero o el valor falso) y que verifica las siguientes propiedades: si P y Q son proposiciones entonces
1. T(P & Q) = True unicamente en el caso de que´ T(P) = True y T(Q) = True.
2. T(P | Q) = False ´unicamente en el caso de que T(P) = False y T(Q) = False.
3. T(-P) = True ´unicamente en el caso de que T(P) = False .
Por tanto: T(P & Q) = T(P) & T(Q), T(P | Q) = T(P) | T(Q) y T(-P) = not T(P). 4. T(P >> Q) = False ´unicamente en el caso en que T(P) = True y T(Q) = False.
5. T(P ** Q) = True ´unicamente en el caso en que T(P) = T(Q).
Por tanto T(P >> Q) = T(-P|Q) y T(P ** Q) = T((P>>Q) & (Q>>P)). Y si A1,...An son nProposiciones, entonces
6. alguno(A1,...An)es VF si y solo si A1 | alguno(A2,...,An)es VF.
7. unoDe(A1,...An)es VF si y solo si (A1 & -alguno(A2,...An) | (-A1 & unoDe(A2,...,An))es VF.
Por tanto
T(alguno(A1,...An)) = T( A1 | alguno(A2,...,An) ) y
T(unoDe(A1,...An)) = T( (A1 & -alguno(A2,...An)) | (-A1 & unoDe(A2,...,An)) )
Cualquier funci´on F que vaya del conjunto de proposicionales at´omicas (es decir, del tipov(”algo”)) al conjunto {True,False}se puede extender al resto proposiciones2formando una valoraci´on T=span(F), aplicando las reglas de arriba. Dicha extensi´on es ´unica.
Vamos a implementar como funciones los diccionarios de Python, pues los diccionarios son una colecci´on de pares
"Etiqueta":valor donde a cada etiqueta solo se le asigna un valor ´unico. Por ejemplo, si definimos el siguiente diccionario; {"a":1 , "a":0 , "b":2}, obtenemos un diccionario con dos elementos, donde a "a" se le a asignado solo uno de los dos dos valores introducidos (en concreto el ´ultimo).
2en la funci´onspande Python hemos elegido el criterio de que a toda proposici´on Prop no incluida expl´ıcitamente en el diccionario F le asignamos el valor False para as´ı completar el dominio de las proposiciones at´omicas. Esta decisi´on ha sido completamente arbitraria, y se ha incluido para que efectivamente el dominio despanincluya todas las proposiciones at´omicas (i.e., variables proposicionales). En cualquier casospanse ha programado a modo de ilustraci´on, pues no la emplearemos para nada.
>>> {"a":1 , "a":0 , "b":2}
{’a’: 0, ’b’: 2}
1.3.1 Funci´on span
Funci´onspan(F, Prop)indica si Prop es True o es False a partir de la funci´on parcial F.
• F: es un diccionario (funci´on parcial) que asigna valores True o False a varias proposiciones at´omicas
• Prop: es unaProposicion
As´ı obtenemos los siguientes resultados l´ogicos:
>>> F={ v("llueve"):True }
>>> span(F, v("llueve") >> -v("llueve") ) False
y por otra parte
>>> span(F, -v("llueve") >> v("llueve") ) True
Implementaci´on
Si una proposici´on Prop (de tipo at´omico) est´a en la funci´on parcial F y adem´as es verdadera,spandevuelve True.
En caso contrario devuelve False (tanto si Prop no est´a en F como si lo est´a pero es False). Si Prop no es de tipo at´omico, entonces se aplican las reglas para reducir Prop a proposiciones de tama˜no m´as peque˜no.
6a hDefinici´on de la funci´on span6ai≡
def span(F, Prop):
if type(Prop) == type(v(’’)):
return (Prop in F) and F[Prop]
hAplicaci´on de las reglas para reducir el tama˜no de Prop6bi
This code is used in chunk2b.
Defines:
span, used in chunk6b.
Uses v5.
6b hAplicaci´on de las reglas para reducir el tama˜no de Prop6bi≡
elif Prop.data[0] == "and":
return span(F, Prop.data[1]) and span(F, Prop.data[2])
elif Prop.data[0] == "or":
return span(F, Prop.data[1]) or span(F, Prop.data[2]) elif Prop.data[0] == "not":
return not span(F, Prop.data[1])
elif Prop.data[0] == "equivale":
return span(F, Prop.data[1]) == span(F, Prop.data[2])
elif Prop.data[0] == "alguno":
return span(F,Prop.data[1]) or span(F,alguno(Prop.data[2:])) if len(Prop.data)>2\
else span(F,Prop.data[1])
elif Prop.data[0] == "unoDe":
A = Prop.data[1]
if len(Prop.data)==2:
return span(F, A) else:
B = Proposicion(["alguno"] + Prop.data[2:]) C = Proposicion(["unoDe"] + Prop.data[2:]) return span(F, (A&-B) | (-A&C) )
This code is used in chunk6a.
Uses alguno4a, Proposicion3a, span6a, and unoDe4a.
1.4 Funci´ on extension que extiende una funci´ on parcial
Ahora queremos definir una funci´on de Python (que denominamosextension) que dadas una lista de objetivos y un diccionario con valores para una colecci´on de proposiciones at´omicas haga crecer (extienda) el diccionario valores con nuevas proposiciones at´omicas, y sus correspondientes valores, necesarios para que se cumplan los pares contenidos en la lista de objetivos; o bien que devuelva el diccionario vac´ıo {} en caso de que sea imposible hacer cumplir dicha lista de objetivos.
La diferencia entre los objetivos y los valores es que en objetivos se asignan valores a proposiciones (arbitrarias) pero en valores solo se asignan valores a proposiciones at´omicas.
El diccionario extendido es un modelo l´ogico en el que se cumplen los objetivos y la funci´on parcial valores. Ello no quiere decir que no puedan existir otros modelos que consigan lo mismo.
Ejemplo:
>>> extension( [ (v(’llueve’) >> -v(’llueve’) , True) ], {v(’basilisco’) : True}) {v(’basilisco’): True, v(’llueve’): False}
Ejemplo:
>>> extension( [ (v(’llueve’),True), (-v(’llueve’),True)], {v(’basilisco’) : True}) {}
1.4.1 Implementaci´on de la funci´on extension
Funci´onextension(objetivo, valores, premisas=[])
• objetivo: es una lista de pares (P,VF) donde P es una proposici´on y VF es True o False
• valores: es un diccionario (funci´on parcial) que asigna valores True o False a variables proposicionales
• premisas: es la lista de premisas a partir de la cual se deduce si una proposici´on es derivable o no (es decir, es solo para usar la operaci´onnoDerivable)
La funci´onextensiontiene dos posibles salidas: o bien devuelve un modelo l´ogico M que extiende a valores de tal forma quespan(M,P)=VFpara cada par (P,VF) de objetivo, o el diccionario vac´ıo {} si no existe tal modelo M. Es decir, busca una forma de dar valores a las proposiciones at´omicas de modo que todas las proposiciones contenidas en el objetivo alcancen el valor deseado.
Implementaci´on
Si la lista objetivo no tiene pares, con la propia funci´on valores, hemos acabado.
La definici´on de la funci´onextensiones recursiva. La mec´anica es la siguiente: se sustituye el proposici´on del primer objetivo por proposiciones de menor tama˜no3, y entonces se llama a si misma. Este procedimiento reduce las proposiciones hasta obtener proposiciones at´omicas.
As´ı pues, llamamos Obj al primer objetivo de la lista, P a la correspondiente proposici´on y VF a su valor objetivo.
Por tanto Obj = (P,VF) es el primer par de la lista objetivo.
Comprobamos que P.data es una list y tratamos los distintos casos de reducci´on de predicado.
8a hDefinici´on de la funci´on extension8ai≡
def extension(objetivo, valores, premisas=[]):
if objetivo == []:
return valores
Obj = objetivo[0]
P = Obj[0]
VF = Obj[1]
if isinstance(P.data,list):
hReducci´on de una proposici´on de tipo ‘‘not”8bi hReducci´on de una proposici´on de tipo ‘‘and”9ai hReducci´on de una proposici´on de tipo ‘‘or”9bi hReducci´on de una proposici´on de tipo ‘‘implica”10ai hReducci´on de una proposici´on de tipo ‘‘equivale”10bi hReducci´on de una proposici´on de tipo ‘‘alguno”10ci hReducci´on de una proposici´on de tipo ‘‘unoDe”11ai hProposici´on de tipo ‘‘noDerivable”11bi
hProposici´on at´omica v12ai
This code is used in chunk2b.
Defines:
extension, used in chunks8–12.
1.4.2 Reducci´on de proposiciones NO at´omicas
• Reducci´on de una proposici´on de tipo “not”.
En el caso de que Obj = (-A,True), nos vale la misma funci´on que obtenemos si remplazamos Obj por (A,False). Y en el caso de que Obj = (-A,False), nos vale la misma funci´on si remplazamos Obj por (A,True).
8b hReducci´on de una proposici´on de tipo ‘‘not”8bi≡
if P.data[0] == "not" : A = P.data[1]
return extension( [(A, not VF)] + objetivo[1:], valores, premisas ) This code is used in chunk8a.
Uses extension8a.
• Reducci´on de una proposici´on de tipo “and”.
En el caso de que Obj=(A & B, True), nos vale la misma funci´on que obtenemos si remplazamos Obj por (A,True), (B,True). Y en el caso de que Obj=(A & B, False), vale tanto la funci´on que obtenemos de remplazar Obj por (A,False) como la que obtenemos al remplazar Obj por (B,False).
Si la primera alternativa resulta en una extensi´on vac´ıa se devuelve la segunda.
9a hReducci´on de una proposici´on de tipo ‘‘and”9ai≡
elif P.data[0] == "and":
A = P.data[1]
B = P.data[2]
if(VF):
return extension([ (A,True), (B,True) ] + objetivo[1:], valores, premisas) else:
r = extension( [(A,False)] + objetivo[1:],valores,premisas)
return extension([(B,False)]+objetivo[1:],valores,premisas) if not r else r
This code is used in chunk8a.
Uses extension8a.
• Reducci´on de una proposici´on de tipo “or”.
En el caso de que Obj=( A|B, False), nos vale la misma funci´on que obtenemos si remplazamos Obj por (A,False), (B,False). Y en el caso de que Obj=( A&B, True), vale tanto la funci´on que obtenemos de remplazar Obj por (A,True) como la que obtenemos al remplazar Obj por (B,True).
Si la primera alternativa resulta en una extensi´on vac´ıa se devuelve la segunda.
9b hReducci´on de una proposici´on de tipo ‘‘or”9bi≡
elif P.data[0] == "or":
A = P.data[1]
B = P.data[2]
if not VF:
return extension([(A,False),(B,False)] + objetivo[1:], valores, premisas) else:
r = extension([(A,True)] + objetivo[1:], valores, premisas)
return extension([(B,True)]+objetivo[1:],valores, premisas) if not r else r
This code is used in chunk8a.
Uses extension8a.
• Reducci´on de una proposici´on de tipo “implica”.
En el caso de que Obj=( A>>B, VF), nos vale la misma funci´on que obtenemos si emplazamos Obj por ( -A|B, VF).
10a hReducci´on de una proposici´on de tipo ‘‘implica”10ai≡
elif P.data[0] == "implica":
A = P.data[1]
B = P.data[2]
return extension([ ( -A|B, VF) ] + objetivo[1:], valores, premisas) This code is used in chunk8a.
Uses extension8a.
• Reducci´on de una proposici´on de tipo “equivale”.
En el caso de que Obj=( A**B, VF), nos vale la misma funci´on que obtenemos si remplazamos Obj por ( (A>>B) & (B>>A), VF).
10b hReducci´on de una proposici´on de tipo ‘‘equivale”10bi≡
elif P.data[0] == "equivale":
A = P.data[1]
B = P.data[2]
return extension([( (A>>B) & (B>>A), VF)] + objetivo[1:], valores, premisas) This code is used in chunk8a.
Uses extension8a.
• Reducci´on de una proposici´on de tipo “alguno”.
En caso de que Obj = (alguno(A), VF) (i.e., la lista de alternativas solo contiene la proposici´on A) nos vale la misma funci´on que (A, VF). Si por el contrario Obj = (alguno(A1,...An) ,VF), con n>1, nos vale la misma funci´on que obtenemos si remplazamos Obj por ( A1|alguno(A2,...,An), VF) (i.e., la primera o alguna de las dem´as) .
10c hReducci´on de una proposici´on de tipo ‘‘alguno”10ci≡
elif P.data[0] == "alguno":
A = P.data[1]
if len(P.data)==2:
return extension([(A,VF)] + objetivo[1:], valores, premisas) else:
B = Proposicion(["alguno"] + P.data[2:])
return extension([( A|B, VF)] + objetivo[1:], valores, premisas) This code is used in chunk8a.
Uses alguno4a, extension8a, and Proposicion3a.
• Reducci´on de una proposici´on de tipo “unoDe”.
En caso de que Obj=(unoDe(A), VF) (i.e., la lista de alternativas solo contiene la proposici´on A) nos vale la
11a hReducci´on de una proposici´on de tipo ‘‘unoDe”11ai≡
elif P.data[0] == "unoDe":
A = P.data[1]
if len(P.data)==2:
return extension([(A,VF)] + objetivo[1:], valores, premisas) else:
B = Proposicion(["alguno"] + P.data[2:]) C = Proposicion(["unoDe"] + P.data[2:])
return extension([( (A&-B) | (-A&C), VF)] + objetivo[1:], valores, premisas) This code is used in chunk8a.
Uses alguno4a, extension8a, Proposicion3a, and unoDe4a.
1.4.3 Proposici´on no derivable de un conjunto de premisas
La funci´onextensiontiene un tercer argumento (premisas) que es un “a˜nadido” para decidir si una proposici´on es noDerivablede la lista de premisas.
Derivable significa que la proposici´on es consecuencia l´ogica de las premisas, por consiguiente es imposible que sea falso si las premisas son ciertas.
11b hProposici´on de tipo ‘‘noDerivable”11bi≡
elif P.data[0] == "noDerivable":
A = P.data[1]
valoresExt = valores.copy() valoresExt[v(repr(P))] = VF
derivable = not extension([(A,False)] + premisas, {}, premisas)
return extension(objetivo[1:],valores2,premisas) if not derivable == VF else {}
This code is used in chunk8a.
Uses extension8a, noDerivable4b, and v5.
1.4.4 ... cuando llegamos a una proposici´on at´omica (o variable proposicional )
En el caso de que Obj = (v(D), VF), es cuando el algoritmo recursivo pasar´a a intentar un nuevo objetivo (extendiendo el diccionario valores si fuera necesario), o bien parar´a por ser el par objetivo (v(D), VF)imposible.
Primero se comprueba si el par (v(D), VF)ya estaba en el diccionario valores, es decir, si valores contiene la proposici´on at´omica v(D) y con el valor VF. En tal caso, se “salta” al siguiente objetivo (es decir se llama al extensioncon el mismo diccionario valores pero quitando de la lista objetivo el primer elemento. Siv(D)pertenece al diccionario valores pero con un valor distinto a VF, quiere decir que el objetivo es imposible de alcanzar, por lo que se devuelve un diccionario vac´ıo, y la recursi´on ha terminado.
Si P es una proposici´on at´omica que no estaba en el diccionario valores, entonces creamos un nuevo diccionario extendido (valoresExt) que incluya P y su valor VF; y entonces llamamos a extension pero quitando el primer objetivo de la lista y usando un diccionario “extendido” (valoresExt). Para evitar los efectos colaterales de modificar un diccionario, modificamos una copia
12a hProposici´on at´omica v12ai≡
else:
if P in valores:
return extension(objetivo[1:], valores, premisas) if valores[P]==VF else {}
else:
valoresExt = valores.copy() valoresExt[P] = VF
return extension(objetivo[1:], valoresExt, premisas) This code is used in chunk8a.
Uses extension8a.
1.5 Funci´ on que refuta una proposici´ on a partir de un conjunto de premisas
Refutar una proposici´on, consiste en asignar un valor a cada variable proposicional (cada proposici´on at´omica), de tal modo que el resultado sea falso.
La funci´on refuta construye un modelo M (asignaci´on de valores a las proposiciones at´omicas) de forma que span(M,P)=False y span(M,h)=True para todo h en premisas.
Si esto no es posible refutar P a partir de las premisas, entonces la funci´onrefuta devuelve un modelo (un diccionario) vac´ıo; esto quiere decir que la proposici´on P es irrefutable, es decir, premisas⇒ P. Es decir, si se refuta es que no es derivable de las premisas (no es consecuencia l´ogica de las premisas).
12b hDefinici´on de la funci´on refuta12bi≡
def refuta(P, premisas=[]):
h=[(Q,True) for Q in premisas]
return extension([(P,False)]+h, {}, h)
This code is used in chunk2b.
Defines:
refuta, used in chunk13a.
Uses extension8a.
1.6 Funci´ on test
Funci´ontest(P,premisas)
• P: es una proposici´on o cualquiera de los valores True, False.
• premisas: es una lista de proposiciones
La funci´ontestse utiliza tanto para saber si se da una precondici´on, como para saber el resultado correcto de una cuesti´on.
En el caso de que P sea una proposici´on, s´olo devuelve True si es imposible refutarlo a partir de las premisas, es
13a hDefinici´on de la funci´on test13ai≡
def test(P,premisas=[]):
if P == True:
return True if P == False:
return False else:
return True if not refuta(P, premisas) else False
This code is used in chunk2b.
Defines:
test, used in chunk19.
Uses refuta12b.
Representaci´ on de la clase Proposicion
13b hM´etodo de representaci´on de la clase Proposicion13bi≡
def __repr__(self):
if isinstance(self.data,list):
if self.data[0] == "and":
return repr(self.data[1]) + " & "+ repr(self.data[2]) if self.data[0] == "or":
return repr(self.data[1]) + " | "+ repr(self.data[2]) if self.data[0] == "not":
return "-" + repr(self.data[1]) if self.data[0] == "implica":
return repr(self.data[1]) + " >> "+ repr(self.data[2]) if self.data[0] == "equivale":
return repr(self.data[1]) + " ** "+ repr(self.data[2]) if self.data[0] == "unoDe":
return "unoDe("+’,’.join([repr(x) for x in self.data[1:]]) +")"
if self.data[0] == "noDerivable":
return "noDerivable("+repr(self.data[1]) +")"
else:
return ’v(’+repr(self.data)+’)’
This code is used in chunk3a.
Uses noDerivable4band unoDe4a.
CREACI ´ON DE PREGUNTAS DE OPCI ´ON M ´ULTIPLE
2.1 Estructura del aparato auxiliar de la librer´ıa prop.py
14 hAparato auxiliar para la creaci´on de problemas de opci´on m´ultiple14i≡
hDefinici´on de la clase Marcador15ai hDefinici´on de la clase Supuesto16bi hDefinici´on de la clase Cuestion17ai hDefinici´on de la clase ProblemaTipo17bi hDefinici´on de la clase ProblemaVF21ai This code is used in chunk2a.
2.2 Clase Marcador
Usaremos la clase Marcador como una herramienta auxiliar para construir el ´arbol con todas las combinaciones posibles entre una conjunto de enunciados y un conjunto de cuestiones. Posteriormente se descartar´an de dicho ´arbol las combinaciones de enunciados y cuestiones que no tengan soluci´on.
ClaseMarcador
• objetivo es una lista de n´umeros naturales mayores que cero
La claseMarcadores un iterador. Si objetivo=[n 1,...,n k] genera todas las listas de la forma [m 1,...m k] tales que cada m i es un n´umero natural menor que n i
>>> for i in Marcador([2,3,1]):
... print(i) ...
[0, 0, 0]
[0, 1, 0]
[0, 2, 0]
[1, 0, 0]
[1, 1, 0]
[1, 2, 0]
Implementaci´on
El atributo self.data es la lista de n´umeros que hemos dado como argumento. En el ejemplo anterior, [2,3,1].
Para construir un iterador necesitamos los m´etodos iter y next .
15a hDefinici´on de la clase Marcador15ai≡
class Marcador:
def __init__(self, data):
self.data = data
hM´etodo iter para la clase Marcador15bi hM´etodo next para la clase Marcador15ci
This code is used in chunk14.
Defines:
Marcador, used in chunk18a.
El atributo self.p contiene la lista que entregar´a next en la siguiente invocaci´on al m´etodo; y el m´etodo iter genera la semilla de dicha lista, en particular dicha semilla es una lista de ceros.
15b hM´etodo iter para la clase Marcador15bi≡
def __iter__(self):
self.p = [0 for x in self.data]
return self This code is used in chunk15a.
next devuelve self.p en su estado actual (n), y coloca en self.p la lista siguiente. Si self.p es vac´ıa detiene la iteraci´on.
15c hM´etodo next para la clase Marcador15ci≡
def __next__(self):
hDefinici´on de la funci´on local Siguiente16ai if self.p == []:
raise StopIteration n = self.p
self.p = Siguiente(self.p, self.data) return n
This code is used in chunk15a.
Dentro del m´etodo next definimos de manera recursiva la funci´on auxiliar local S, que calcula la lista siguiente.
Funci´on auxiliar Siguiente (x, y)
• x: lista de n´umeros naturales [m 1...m r] tales que m i <n i (es la lista de la que queremos calcular la siguiente).
• y: lista de n´umeros naturales [n 1...n r] mayores que cero (lista de topes).
Devuelve [k 1...k r] la siguiente lista a [m 1...m r] (seg´un el orden lexicogr´afico) tal que k i<n i. En caso de no existir tal lista devuelve [].
16a hDefinici´on de la funci´on local Siguiente16ai≡
def Siguiente(x,y):
if x == [] : return []
s = Siguiente(x[1:],y[1:]) if s == []:
if x[0]+1 == y[0]:
return []
else:
return [x[0]+1] + [0 for i in x[1:]]
else:
return [x[0]] + s This code is used in chunk15c.
2.3 Las clases Supuesto, Cuestion y ProblemaTipo
2.3.1 Clase Supuesto(enunciado, semantica, precond)
ClaseSupuesto(enunciado, semantica, precond)
Es un constructor que sirve para almacenar tres aspectos (atributos) del supuesto de un problema.
• enunciado: string que presenta la supuesto en lenguaje humano.
• semantica: proposici´on que traduce parcialmente el significado l´ogico del enunciado (contiene lo necesario para deducir las respuestas).
• precond: es una proposici´on o bien cualquiera de los valores True, False. ElSupuesto ser´a incluido en el problema solo si la evaluaci´on de precond es True.
16b hDefinici´on de la clase Supuesto16bi≡
class Supuesto:
def __init__(self,enunciado, semantica, precond=True):
self.e = enunciado self.s = semantica self.p = precond This code is used in chunk14.
Defines:
Supuesto, used in chunks19,20,28,34b,38, and41.
2.3.2 Clase Cuestion(enunciado, semantica, precond)
ClaseCuestion(enunciado, semantica, precond)
Es un constructor que sirve para almacenar tres aspectos (atributos) de una cuesti´on de un problema.
• semantica: proposici´on P tal quetest(P, sem´antica del conjunto de supuestos) devuelve la respuesta correcta.
• precond: es una proposici´on o bien cualquiera de los valores True, False. La Cuestion ser´a incluida en el problema solo si la evaluaci´on de precond es True.
17a hDefinici´on de la clase Cuestion17ai≡
class Cuestion:
def __init__(self, enunciado, semantica, precond=True):
self.e = enunciado self.s = semantica self.p = precond This code is used in chunk14.
Defines:
Cuestion, used in chunks19,20,28,34b,38, and41.
2.3.3 Clase ProblemaTipo
ClaseProblemaTipo (supuestos y cuestiones)
• supuestos y cuestiones: es una lista de listas de strings,Supuestos, oCuestiones.
Cada lista representa un conjunto de opciones excluyentes. Para mayor comodidad, en el caso de que una de esas listas consista en un ´unico objeto (una ´unica opci´on), tambi´en se permite escribir directamente el objeto sin encerrarlo en una lista, es decir, la lista supuestos y cuestiones tambi´en admite objetos de tipo: string, Supuesto o Cuestion.
Implementaci´on La claseProblemaTipoes un iterador que genera todas las versiones aceptables de un problema.
Cada variante resulta de tomar una de las opciones de cada una de las listas en supuestos y cuestiones, pero si la precondici´on de alguna de las opciones elegidas es False la correspondiente variante del problema es rechazada.
• El atributo self.e contiene la lista supuestos y cuestiones. Cada elemento de supuestos y cuestiones es a su vez una lista de opciones (excluyentes) correspondiente a cada componente (cada parte) del texto del problema. La combinaci´on de distintas opciones crea el texto completo de una variante del problema.
El m´etodo iter inicializa el iterador. El m´etodo next obtiene la siguiente variante del problema, pero ase- gur´andose de que las precondiciones son satisfechas (si no la variante es desechada).
17b hDefinici´on de la clase ProblemaTipo17bi≡
class ProblemaTipo:
def __init__(self, supuestos_y_cuestiones):
self.e = supuestos_y_cuestiones hM´etodo iter para la clase ProblemaTipo18ai hM´etodo next para la clase ProblemaTipo18bi
This code is used in chunk14.
Defines:
ProblemaTipo, used in chunks20,28,34b,38, and41.
El atributo self.l es la lista self.e normalizada para que todos sus objetos sean listas; self.long es el n´umero de componentes que constituyen el texto de cada variante. self.i es el iterador y self.c un contador.
18a hM´etodo iter para la clase ProblemaTipo18ai≡
def __iter__(self):
self.l = [x if isinstance(x,list) else [x] for x in self.e]
self.long = len(self.l)
self.i = iter(Marcador([len(x) for x in self.l])) self.c = 0
return self This code is used in chunk17b.
Uses Marcador15a.
La generaci´on de variantes de unProblemaTipo se produce dentro de un bucle que solo se detiene cuando el intento (try) de iterar una vez m´as, next(self.i) falla por haberse agotado todas las variantes.
self.longguarda el n´umero de elementos (es decir, de cadenas,Supuestos yCuestiones) que constituyen el texto del problema.
En cada iteraci´on 1) enunciado, hipotesis y cuestiones comienzan estando vac´ıas; y 2) se recorren uno a uno los componentes de la variante que se intenta construir.
Una vez se han recorrido todos los componentes se devuelve la lista: (str(self.c), enunciado, cuestiones) que constituye la variante concreta del problema que se ha generado en esa iteraci´on. Cada tipo de componente es tratado de una forma distinta.
18b hM´etodo next para la clase ProblemaTipo18bi≡
def __next__(self):
self.c += 1
while True:
try:
variante = next(self.i) except StopIteration:
raise StopIteration
enunciado = ""
hipotesis = []
cuestiones = []
for n in range(self.long+1):
if n == self.long:
return (str(self.c), enunciado, cuestiones)
componente = self.l[n][variante[n]]
hTratamiento en funci´on del tipo de componente19i This code is used in chunk17b.