</> Cómo funciona

singularizar

GET
/v1/singularizar

Función pura del español.

Parámetros

parámetrotipopor defectoqué hace
palabrastring— (obligatorio)la palabra en plural

Cómo funciona (por dentro)

Mapa de funciones

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;

El código

# -*- 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)…]

Pruébalo

Llámala con el código a la vista.

Ejemplos con el SDK

curl "https://api.wordkers.com/v1/singularizar" \
  -H "X-API-Key: TU_CLAVE"

Cómo empezar con el SDK →

¿Quieres que cambie algo, o copiar la lógica para lo tuyo? Sin problema — escríbeme. Lo que ves es el algoritmo; los datos y las claves se quedan en casa.

Wordkers · Catálogo · Empezar · Tu cuenta