Ilustración conceptual de una función que recibe datos de una puerta y un catálogo y entrega una clasificación.

Funciones: separar una regla de todo lo que ocurre alrededor

La regla para revisar un código de puerta cabe en unas pocas decisiones. La versión que consulta Revit, busca el catálogo, corrige el parámetro, escribe un informe y muestra una ventana ya necesita bastante más conversación. Ponerle a todo eso el nombre procesar_puerta no ha simplificado el trabajo: solo le ha colocado una cortina con un nombre respetable.

Vamos a aislar una clasificación que recibe un código y un catálogo y devuelve un estado. La misma regla podrá utilizarse para mostrar una respuesta, preparar un listado o probar casos sin abrir el modelo. Una función permite dar nombre a ese trabajo y llamarlo con entradas concretas. Es un encargo modestísimo para mi talento, pero bastante más útil que repartir la misma condición por cinco botones y esperar que todos envejezcan igual.

Tabla de contenidos

El nombre debe explicar qué trabajo se está pidiendo

Una función reúne instrucciones que podemos ejecutar mediante una llamada. Sus parámetros nombran las entradas que necesita; el resultado permite entregar información a quien la llamó. Esta separación hace visible un contrato: con qué datos se utiliza, qué interpreta y qué devuelve. No exige que todas las funciones calculen algo ni que todas sean pequeñas por una cifra mágica. Exige que podamos explicar su responsabilidad sin enumerar media aplicación.

En nuestro caso clasificar_codigo recibirá texto o nulo y una colección de códigos admitidos. Devolverá no evaluable para un nulo, incumple para un texto vacío o compuesto solo por espacios, y cumple únicamente cuando el texto original pertenezca al catálogo. No normalizará un código con contenido: " D01 " seguirá siendo distinto de "D01". El contrato no admite números como códigos; la lectura de la fuente debe entregar el tipo acordado.

Esta regla retoma las decisiones de condicionales y las convierte en una operación independiente. No vamos a utilizar una función como excusa para repetir toda aquella explicación. Aquí interesa qué recibe, qué devuelve y qué queda fuera. El catálogo llega como entrada porque cambiarlo es una decisión del proceso, no una sorpresa que la función deba encontrar en otra parte del programa.

				
					def clasificar_codigo(codigo, catalogo):
    if codigo is None:
        return "no evaluable"
    if codigo.strip() == "":
        return "incumple"
    if codigo in catalogo:
        return "cumple"
    return "incumple"

catalogo = {"D01", "D02"}
print(clasificar_codigo("D01", catalogo))
print(clasificar_codigo("X99", catalogo))
print(clasificar_codigo(None, catalogo))
				
			

Puedes guardar este programa en un archivo .py y ejecutarlo con Python 3.12. Las salidas son cumple, incumple y no evaluable. def introduce la definición y la sangría delimita su cuerpo. Esa definición no clasifica por sí sola todas las puertas: cada llamada entrega un valor concreto a los parámetros y ejecuta las instrucciones necesarias para obtener su resultado. Hemos separado describir el trabajo de pedir que se haga.

Devolver un resultado permite decidir qué hacer después

return termina esa ejecución de la función y entrega el valor indicado. La primera llamada encuentra D01 en el catálogo y devuelve cumple; una entrada nula termina antes, en su primera condición. No necesitamos un else que envuelva todo lo que queda después de cada retorno. Las salidas tempranas pueden facilitar la lectura cuando apartan un caso bien definido, siempre que siga siendo sencillo comprobar que todos los caminos devuelven lo prometido.

La guía de funciones de Python explica la definición, los argumentos y el retorno. Una diferencia que merece detenerse es la que hay entre imprimir y devolver. print produce una salida visible; no convierte ese texto en el resultado de la función. El fragmento siguiente es independiente y muestra el fallo de una función que anuncia una clasificación, pero no se la entrega a quien la llamó.

				
					def mostrar_estado():
    print("cumple")

resultado = mostrar_estado()
print(resultado is None)
				
			

Verás cumple y después True. La llamada no devuelve explícitamente un valor y su resultado es None. Si otro paso esperaba comparar resultado con "cumple", el mensaje de pantalla no lo sustituye. El programa ha comunicado algo a quien lo mira y otra cosa a quien lo utiliza. Una doble personalidad muy poco conveniente para una regla de validación.

No hay nada incorrecto en una función cuya tarea sea mostrar un mensaje. El problema es prometer un resultado calculado y entregar solo un efecto visible. Yo mantendría la clasificación libre de mensajes de interfaz y dejaría la presentación a quien tenga que escoger idioma, formato o destino. Así la misma respuesta sirve para una consola, una tabla o una prueba sin que cada lector tenga que interceptar lo que escribió la función en pantalla.

La colección organiza las llamadas, la función conserva la regla

Una vez aislada la clasificación, el recorrido de puertas puede encargarse de llamar a la función y asociar su respuesta con el identificador. Añade el siguiente bloque debajo del primer programa Python. Utiliza la función y el catálogo definidos allí; no es un archivo independiente. Las parejas de entrada son ficticias y mantienen juntos el identificador de la puerta y el código que vamos a evaluar.

				
					puertas = [("P01", "D01"), ("P02", ""), ("P03", None), ("P04", "X99")]
informe = []
for identificador, codigo in puertas:
    estado = clasificar_codigo(codigo, catalogo)
    informe.append((identificador, estado))

for identificador, estado in informe:
    print(identificador, estado)
				
			

Este bloque añade cuatro líneas: P01 cumple, P02 incumple, P03 no evaluable y P04 incumple. El recorrido no vuelve a escribir las condiciones del catálogo. La función no sabe si la puerta se llama P01 ni en qué fila aparecerá el resultado; recibe el código que necesita clasificar. La lista informe conserva las asociaciones para poder utilizar después la salida sin repetir la revisión.

Si mañana cambia una decisión de clasificación, habrá un lugar reconocible que revisar. Eso no evita estudiar a quién afecta el cambio, pero impide que cada botón mantenga una versión distinta de la misma regla. Copiar condiciones parece una forma rápida de avanzar hasta que una se corrige y las otras conservan su criterio anterior. Entonces la aplicación empieza a discutir consigo misma según por dónde entre el usuario.

La responsabilidad también tiene un límite por el otro lado. Una función por cada comparación puede obligarnos a saltar entre diez nombres para entender una regla de cuatro condiciones. No dividiría por número de líneas ni por ceremonia arquitectónica. Separaría una operación cuando su nombre, sus entradas y su resultado permitan entender o probar algo útil sin leer todo el resto del proceso.

C# declara los tipos de las entradas y del resultado

En una consola de .NET 8 podemos expresar el mismo contrato con una función local estática. string? admite un código nulo, HashSet<string> contiene el catálogo y el string inicial declara el tipo devuelto. static impide que esta función capture variables locales del código que la contiene: las entradas utilizadas deben llegar por los parámetros o por otros accesos permitidos por el lenguaje. Aquí solo utiliza sus parámetros.

				
					using System;
using System.Collections.Generic;

var catalogo = new HashSet<string>(new[] { "D01", "D02" }, StringComparer.Ordinal);
var puertas = new (string Id, string? Codigo)[]
{
    ("P01", "D01"), ("P02", ""), ("P03", null), ("P04", "X99")
};
foreach (var puerta in puertas)
{
    string estado = ClasificarCodigo(puerta.Codigo, catalogo);
    Console.WriteLine($"{puerta.Id} {estado}");
}

static string ClasificarCodigo(string? codigo, HashSet<string> catalogo)
{
    if (codigo is null) return "no evaluable";
    if (codigo.Trim() == "") return "incumple";
    if (catalogo.Contains(codigo)) return "cumple";
    return "incumple";
}
				
			

Guarda el programa completo como Program.cs en un proyecto de consola dirigido a .NET 8. Obtendrás las cuatro líneas del informe Python. La comparación ordinal mantiene la distinción entre mayúsculas y minúsculas en nuestros códigos de ejemplo. La comprobación de espacios utiliza Trim, pero la pertenencia consulta el texto original, de manera que no se introduce una normalización silenciosa.

Una función local de C# pertenece al miembro que la contiene; este ejemplo no pretende definir la arquitectura completa de una biblioteca compartida. Si varios componentes necesitasen la misma regla, decidiríamos dónde declararla para hacerla accesible de forma apropiada. El fundamento que queremos conservar es el contrato explícito, no una ubicación que haya que copiar obligatoriamente en cualquier proyecto.

Declarar string como resultado evita devolver por accidente un número, pero sigue permitiendo errores de significado o de escritura en los estados. Más adelante podremos elegir una representación más restringida. Por ahora basta con observar qué protege el tipo y qué permanece como acuerdo de nuestra función. Un compilador comprueba muchas cosas; que la palabra «cumple» corresponda a la política correcta del proyecto no está entre sus poderes adivinatorios.

Prueba la regla antes de conectarla a una escritura

La función de clasificación no consulta archivos, no muestra mensajes, no modifica el catálogo y no escribe en Revit. Para los mismos valores de entrada devuelve el mismo estado. Ese alcance permite probar una decisión sin depender de la disponibilidad de la interfaz o del modelo. Si otra parte modifica el catálogo entre llamadas, los valores de entrada ya han cambiado, aunque el nombre de la variable siga siendo el mismo.

Las pruebas útiles incluyen D01, X99, nulo, cadena vacía, solo espacios y un código con espacios exteriores. También probaría D01 con un catálogo que solo contiene D02: debe incumplir. Así comprobamos que la función utiliza el catálogo recibido en lugar de una copia oculta fija en el programa. Comparar únicamente el caso admitido demostraría muy poco sobre la independencia que acabamos de prometer.

El siguiente paso podría leer códigos de una fuente real y entregar cada uno a esta función, pero esa lectura tendría sus propias condiciones de acceso, unidades cuando proceda y errores. Una escritura necesitaría además permisos, validación de cambios y reglas de la API. Separar esas responsabilidades permite comprobarlas en su lugar. No convierte por arte de magia una función de consola en una integración probada con el modelo.

Antes de añadir otra operación a una función existente, intenta describir su resultado sin incluir todos los destinos donde se utilizará. Si «clasificar un código» obliga también a abrir una ventana o modificar la puerta, la decisión ya lleva equipaje que no necesita para responder. Dale entradas claras, pide un resultado y decide los efectos después. La función no debería necesitar acceso a medio proyecto para decirnos si D01 está en una lista que ya le hemos entregado.

Artículos relacionados

Respuestas

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *