La programación tácita , también llamada estilo sin puntos , es un paradigma de programación en el que las definiciones de funciones no identifican los argumentos (o "puntos") sobre los que operan. En cambio, las definiciones simplemente componen otras funciones, entre las que se encuentran combinadores que manipulan los argumentos. La programación tácita es de interés teórico, porque el uso estricto de la composición da como resultado programas que se adaptan bien al razonamiento ecuacional . [1] También es el estilo natural de ciertos lenguajes de programación , incluidos APL y sus derivados, [2] y lenguajes concatenativo como Forth . La falta de denominación de argumentos le da al estilo sin puntos una reputación de ser innecesariamente oscuro, de ahí el epíteto de "estilo sin puntos". [1]
Los scripts de Unix utilizan el paradigma con tuberías .
La programación tácita se puede ilustrar con el siguiente código Python . Una secuencia de operaciones como la siguiente:
def ejemplo ( x ): return baz ( bar ( foo ( x )))
... puede escribirse en estilo libre de puntos como la composición de una secuencia de funciones, sin parámetros: [3]
de functools importar parcial , reducir def componer ( * fns ): devolver parcial ( reducir , lambda v , fn : fn ( v ), fns )ejemplo = componer ( foo , bar , baz )
Para un ejemplo más complejo, el código Haskell p = ((.) f) . g
se puede traducir como:
p = parcial ( componer , parcial ( componer , f ), g )
Un ejemplo sencillo (en Haskell ) es un programa que calcula la suma de una lista de números. Podemos definir la función suma de forma recursiva utilizando un estilo puntual (cf. programación a nivel de valores ) como:
suma ( x : xs ) = x + suma xs suma [] = 0
Sin embargo, utilizando un pliegue podemos reemplazar esto con:
suma xs = foldr ( + ) 0 xs
Y entonces el argumento no es necesario, por lo que esto se simplifica a
suma = foldr ( + ) 0
que no tiene puntos.
Otro ejemplo utiliza la composición de funciones :
pxyz = f ( gxy ) z
El siguiente pseudocódigo similar a Haskell expone cómo reducir una definición de función a su equivalente sin puntos:
p = \ x -> \ y -> \ z -> f ( g x y ) z = \ x -> \ y -> f ( g x y ) = \ x -> \ y -> ( f . ( g x )) y = \ x -> f . ( g x ) ( * Aquí se utiliza el operador de composición infijo "." como una función currificada . * ) = \ x -> (( . ) f ) ( g x ) = \ x -> ((( . ) f ) . g ) x p = (( . ) f ) . g
Finalmente, para ver un ejemplo complejo, imaginemos un programa de filtro de mapas que toma una lista, le aplica una función y luego filtra los elementos según un criterio.
lista de operadores de criterios mf = criterios de filtro ( lista de operadores de mapa )
Puede expresarse sin puntos [4] como
mf = ( . mapa ) . ( . ) . filtro
Obsérvese que, como se indicó anteriormente, los puntos en "sin puntos" se refieren a los argumentos, no al uso de puntos; un error común. [5]
Se han escrito algunos programas para convertir automáticamente una expresión Haskell a una forma sin puntos.
En J , el mismo tipo de código sin puntos aparece en una función creada para calcular el promedio de una lista (matriz) de números:
promedio =: +/ % #
+/
suma los elementos de la matriz asignando ( /
) suma ( +
) a la matriz. %
divide la suma por el número de elementos ( #
) en la matriz.
La fórmula de Euler expresada tácitamente:
porque =: 2 o . ] pecado =: 1 o . ] Euler =: ^@ j . = porque j . pecado
( j.
es una función primitiva cuya definición monádica es 0j1
multiplicada por x y cuya definición diádica es x+0j1×y
.) Los mismos cálculos tácitos expresados en Dyalog APL :
promedio ← + ⌿ ÷ ≢ cos ← 2 ○ ⊢ sin ← 1 ○ ⊢ EulerCalc ← cos + 0j1 × sin ⍝ 0j1 es lo que usualmente se escribe como i EulerDirect ← * 0J1 ×⊢ ⍝ Igual que ¯12○⊢ ⍝ ¿Los 2 métodos producen el mismo resultado? EulerCheck ← EulerDirect = EulerCalc EulerCheck ¯1 1 2 3 1 1 1 1 ⍝ ¡Sí, hasta ahora todo bien!
En los lenguajes de programación orientados a la pila (y en los concatenativo , la mayoría de los cuales se basan en la pila [ cita requerida ] ), se utilizan comúnmente métodos sin puntos. Por ejemplo, un procedimiento para calcular los números de Fibonacci podría verse como el siguiente en PostScript :
/fib { dup dup 1 eq intercambio 0 eq o no { dup 1 sub fib intercambio 2 sub fib agregar } si } def
En los scripts de Unix, las funciones son programas informáticos que reciben datos de la entrada estándar y envían los resultados a la salida estándar . Por ejemplo,
ordenar | uniq -c | ordenar -rn
es una composición tácita o sin puntos que devuelve los recuentos de sus argumentos y los argumentos, en orden de recuento decreciente. 'sort' y 'uniq' son las funciones, '-c' y '-rn' controlan las funciones, pero no se mencionan los argumentos. La barra vertical '|' es el operador de composición.
Debido a la forma en que funcionan las canalizaciones, normalmente solo es posible pasar un "argumento" a la vez en forma de un par de secuencias de entrada/salida estándar. Aunque se pueden abrir descriptores de archivos adicionales desde canalizaciones con nombre , esto ya no constituye un estilo sin puntos.
jq es un lenguaje de programación orientado a JSON en el que se utiliza el símbolo '|' para conectar filtros y formar una secuencia de comandos de una forma habitual. Por ejemplo:
[1,2] | añadir
evalúa a 3. (Sí, la matriz JSON es un filtro jq que evalúa a una matriz).
Aunque son similares a las canalizaciones de Unix, las canalizaciones de jq permiten enviar los datos entrantes a más de un destinatario en el lado derecho del '|' como si fuera en paralelo. Por ejemplo, el programa `add/length` calculará el promedio de los números en una matriz, de modo que:
[1,2] | añadir/longitud
evalúa a 1,5
Similarmente:
[1,2] | [longitud, añadir, añadir/longitud]
evalúa a [2,3,1.5]
Se puede utilizar un punto ('.') para definir un punto de conexión en el lado derecho, por ejemplo:
1 | [., .]
evalúa a [1,1]
y de manera similar:
2 | poder(.; .)
se evalúa como 4 ya que pow(x;y) es x elevado a y.
Un programa jq tácito para generar la secuencia de Fibonacci sería:
[0,1] | recurse( [último, agregar] ) | primero
Aquí, [0,1] es el par inicial que debe tomarse como los dos primeros elementos de la secuencia de Fibonacci. (El par [1,1] también podría utilizarse para la definición de la variante).
Los tokens alfabéticos son filtros incorporados: `first` y `last` emiten el primer y último elemento de sus matrices de entrada respectivamente; y `recurse(f)` aplica un filtro, f, a su entrada de forma recursiva.
jq también permite definir nuevos filtros en un estilo tácito, por ejemplo:
def fib: [0,1] | recurse( [último, agregar] ) | primero;
En la sección sobre Python de este artículo, se considera la siguiente definición de Python:
def ejemplo ( x ): return baz ( bar ( foo ( x )))
En un estilo sin puntos, esto podría escribirse en Python como:
ejemplo = componer ( foo , bar , baz )
En jq, la definición equivalente sin puntos sería:
def ejemplo: foo | bar | baz;