</> Cómo funciona
Función pura del español.
| parámetro | tipo | por defecto | qué hace |
|---|---|---|---|
palabra | string | — (obligatorio) | la palabra en plural |
commons/singularizador.py → función singularizar().upln) para recolocar la tilde al quitar -es (árboles → árbol) y resolver las terminaciones -íes/-úes.flowchart TD
MOD[("repositorio")]
_regla_excepciones["_regla_excepciones()"]
_regla_compuesta["_regla_compuesta()"]
_regla_termina_x["_regla_termina_x()"]
_regla_ces["_regla_ces()"]
_regla_ies["_regla_ies()"]
_regla_is["_regla_is()"]
_regla_yes["_regla_yes()"]
_regla_ys["_regla_ys()"]
_regla_consonante_s["_regla_consonante_s()"]
_regla_ies_ues["_regla_ies_ues()"]
_regla_general_s["_regla_general_s()"]
_regla_termina_s["_regla_termina_s()"]
singularizar["singularizar()"]
_regla_compuesta --> singularizar
_regla_termina_s --> _regla_ces
_regla_excepciones -. datos .-> MOD
_regla_termina_x -. datos .-> MOD
_regla_ces -. datos .-> MOD
_regla_ies -. datos .-> MOD
_regla_is -. datos .-> MOD
_regla_yes -. datos .-> MOD
_regla_ys -. datos .-> MOD
_regla_consonante_s -. datos .-> MOD
_regla_ies_ues -. datos .-> MOD
_regla_general_s -. datos .-> MOD
singularizar -. datos .-> MOD
classDef red fill:#fde7f0,stroke:#c0397b,color:#7a1e4d;
class MOD red;# -*- coding: utf-8 -*-
"""
singularizador.py — pasa una palabra de plural a singular (la operación REVERSA del plural).
Ir de plural a singular es AMBIGUO: una misma forma plural puede venir de varios
singulares, así que a veces se devuelven varias opciones separadas por coma (y puede
incluir la propia palabra). Uso típico: normalizar la entrada a su forma canónica.
Orden de reglas: excepciones → compuesta(-) → termina en X → termina en S
(ces / ies / is / yes / ys / consonante+es / íes-úes / general -s).
Se apoya en el silabeador/acentuador algorítmico (upln).
"""
import json
import os
import upln
# […carga de excepciones de singular (repositorio léxico, interno)…]
def _regla_excepciones(p):
# Formas irregulares que no siguen ninguna regla general: se resuelven de tabla.
return _EXC.get(p)
def _regla_compuesta(p):
# Palabras con guion (p.ej. "coches-cama"): se singulariza cada parte y se combinan.
partes = p.split("-")
if len(partes) <= 1:
return None
if len(partes) == 2:
opts = [f"{s}-{partes[1]}" for s in singularizar(partes[0]).split(",")]
res = ",".join(opts)
for s in singularizar(partes[1]).split(","):
res += f",{partes[0]}-{s}"
return res
else:
res = f"{singularizar(partes[0])}-{partes[1]}-{partes[2]}"
res += f",{partes[0]}-{singularizar(partes[1])}-{partes[2]}"
return res
def _regla_termina_x(p):
# Palabra llana o esdrújula acabada en -x = INVARIABLE (tórax, clímax): singular = plural.
sil = upln.silabear(p)
ns = upln.numero_silabas(sil)
if p.endswith("x") and ns >= 2 and upln.int_silaba_tonica(sil) != 1:
return p
return None
def _regla_ces(p):
# -ces puede venir de -ce (luces→luce) o de -z (luces→luz): se ofrecen ambas.
sil = upln.silabear(p)
pos = upln.int_silaba_tonica(sil)
if p.endswith("ces"):
r = p[:-1] # luces -> luce
if pos > 1:
r += "," + upln.remplace_last("ces", "z", p) # + luz
return r
return None
def _regla_ies(p):
# -ies puede venir de -ie o de -y (reyes/leyes se tratan aparte por excepción).
if p.endswith("ies"):
return p[:-1] + "," + upln.remplace_last("ies", "y", p)
return None
def _regla_is(p):
if p.endswith("is"):
return p[:-1] + "," + upln.remplace_last("is", "y", p)
return None
def _regla_yes(p):
# -yes viene de -y (reyes→rey) o de -ye.
if p.endswith("yes"):
return p[:-1] + "," + p[:-2]
return None
def _regla_ys(p):
if p.endswith("ys"):
return p[:-1]
return None
def _regla_consonante_s(p):
# -es tras consonante = plural de palabra acabada en consonante (árboles→árbol):
# se quita -es y se recoloca la tilde con el acentuador.
if len(p) <= 2 or not p.endswith("es"):
return None
if not upln.is_consonant(p[-3]):
return None
sil = upln.silabear(p)
pos = upln.int_silaba_tonica(sil)
r = p[:-1] # árboles -> árbole
aux = upln.remplace_last("es", "", sil)
aux = upln.remplace_last(upln.SEPARADOR, "", aux)
aux = upln.desacentuar_palabra(aux)
if upln.get_num_vocales(aux) > 0:
r = r + "," + upln.acentuar_silabeada(aux, pos - 1) # + árbol
return r + "*" # marcador temporal (se limpia al final)
def _regla_ies_ues(p):
# -íes / -úes (jabalíes→jabalí, menúes→menú): vocal tónica + es.
if p.endswith("íes") or p.endswith("úes"):
sil = upln.silabear(p)
pos = upln.int_silaba_tonica(sil)
r = p[:-1]
aux = upln.remplace_last(upln.SEPARADOR + "es", "", sil)
aux = upln.desacentuar_palabra(aux)
return r + "," + upln.acentuar_silabeada(aux, pos - 1)
return None
def _regla_general_s(p):
# Caso general: se quita la -s final (casas→casa).
if p.endswith("s"):
return p[:-1]
return None
def _regla_termina_s(p):
# Orquesta las variantes de terminación en -s en orden de especificidad.
aux = _regla_ces(p)
if aux is not None:
return p + "," + aux # ces incluye la propia palabra
for regla in (_regla_ies, _regla_is, _regla_yes, _regla_ys,
_regla_consonante_s, _regla_ies_ues, _regla_general_s):
aux = regla(p)
if aux is not None:
return aux
return None
def singularizar(palabra):
"""Devuelve el/los singular(es) de `palabra` (varias opciones separadas por coma)."""
if palabra is None:
return None
palabra = palabra.strip().lower()
if palabra == "":
return ""
result = palabra # por defecto, la propia palabra
# Se prueban las reglas en orden; la primera que responde, gana.
for regla in (_regla_excepciones, _regla_compuesta, _regla_termina_x, _regla_termina_s):
aux = regla(palabra)
if aux is not None:
result = aux
break
return result.replace("*", "") # limpia el marcador temporal
if __name__ == "__main__":
# […pruebas del módulo (casos de ejemplo y comprobación)…]
Llámala con el código a la vista.
curl "https://api.wordkers.com/v1/singularizar" \
-H "X-API-Key: TU_CLAVE"