Esta página lleva la tesis del programa hasta el final: aprende el representante, reconoce la
familia entera. El mismo problema de la clase —la suma de los enteros de 1 a n— resuelto por los
primos de cada familia del Atlas, no solo por los diez lenguajes del
núcleo.
Un problema tan pequeño es aquí una herramienta de precisión: como cabe entero en cuatro líneas, lo único que queda a la vista es la forma de organizar la solución. Y esa forma es exactamente lo que llamamos paradigma. Verás el mismo cálculo escrito como bucle, como pliegue, como vector y como regla lógica, y ninguna de esas versiones es una traducción de la otra: son maneras distintas de pensar el problema.
⚠️ Qué está verificado y qué no. Ruby, Perl y Lua se ejecutan en CI contra el mismo
casos.jsonque el núcleo, igual que las diez implementaciones de la clase (workflow Labs). Los otros 17 primos son material de lectura: su toolchain no está en el workflow, así que están escritos para ser correctos pero sin el sello de la máquina. Verificar tres de veinte no es verificarlos todos.
n (con n >= 1)suma=<1+2+...+n>suma = 1 + 2 + ... + n| stdin | esperado |
|---|---|
5 |
suma=15 |
3 |
suma=6 |
1 |
suma=1 |
Representantes del núcleo: Python · PHP. Son lenguajes multiparadigma por diseño: no te obligan a elegir. Por eso, dentro de esta misma familia, el problema aparece resuelto de tres maneras distintas.
n = STDIN.gets.to_i
puts "suma=#{(1..n).sum}"
my $n = <STDIN>;
my $suma = 0;
$suma += $_ for 1 .. $n;
print "suma=$suma\n";
local n = tonumber(io.read("l"))
local suma = 0
for i = 1, n do
suma = suma + i
end
print("suma=" .. suma)
gets stdin n
set suma 0
for {set i 1} {$i <= $n} {incr i} {
incr suma $i
}
puts "suma=$suma"
n <- as.integer(readLines("stdin", n = 1))
cat(sprintf("suma=%d\n", sum(1:n)))
Qué reconocer: aquí está la lección entera de la clase en cinco programas. Lua y Tcl escriben un
bucle imperativo: hay una variable que cambia de valor en cada vuelta. Ruby no escribe bucle
ninguno: pide al objeto rango (1..n) que se sume a sí mismo, porque en Ruby hasta un rango es
un objeto con métodos. R no escribe bucle ni objeto: construye el vector 1:n y lo agrega de
golpe, que es el paradigma vectorial que heredó de S y del cálculo estadístico. Perl queda en medio
con su for de modificador. Mismo lenguaje-familia, mismo resultado, tres modelos mentales
diferentes: el paradigma no es una propiedad del lenguaje, es la manera de organizar la solución
que el lenguaje te hace cómoda.
Representantes del núcleo: JavaScript · TypeScript.
import 'dart:io';
void main() {
final n = int.parse(stdin.readLineSync()!.trim());
var suma = 0;
for (var i = 1; i <= n; i++) {
suma += i;
}
print('suma=$suma');
}
// ActionScript 3 corre en el reproductor Flash: no hay stdin ni consola,
// así que se muestra el mismo cálculo dentro de un método de clase.
package {
public class Suma {
public static function hasta(n:int):String {
var suma:int = 0;
for (var i:int = 1; i <= n; i++) {
suma += i;
}
return "suma=" + suma;
}
}
}
Qué reconocer: los dos escriben el bucle for clásico heredado de C, con las mismas tres partes
(inicio, condición, avance). Pero fíjate en el envoltorio, que es lo paradigmático: Dart permite
una función main suelta, mientras que ActionScript 3 obliga a meter todo dentro de una clase dentro
de un paquete. Ese "todo tiene que ser un método de algo" es la herencia de Java sobre la familia
web, y es una decisión de paradigma, no de sintaxis.
Representante del núcleo: Java. La misma máquina virtual, el mismo bytecode y la misma biblioteca estándar sirven de soporte a cuatro paradigmas distintos.
fun main() {
val n = readLine()!!.trim().toInt()
println("suma=${(1..n).sum()}")
}
object Suma extends App {
val n = scala.io.StdIn.readLine().trim.toInt
println(s"suma=${(1 to n).sum}")
}
def n = System.in.newReader().readLine().trim() as int
println "suma=${(1..n).sum()}"
(let [n (Integer/parseInt (.trim (read-line)))]
(println (str "suma=" (reduce + (range 1 (inc n))))))
Qué reconocer: esta familia es la prueba más limpia de que el paradigma no lo impone la
plataforma. Los cuatro producen bytecode para la misma JVM que el for de Java de la clase, y sin
embargo ninguno escribe un bucle: los tres primeros piden a un rango que se sume, y Clojure lo dice
en voz alta con reduce +, que es el pliegue del paradigma funcional: una operación binaria
aplicada acumulativamente sobre una secuencia. Java necesita una variable suma que cambie de valor;
Clojure no tiene ninguna variable que cambie. Esa diferencia —hay estado mutable o no lo hay— es la
frontera paradigmática que esta parte del curso va a recorrer entera.
Representante del núcleo: C#. Dos lenguajes del mismo runtime, nacidos de dos tradiciones opuestas.
let n = stdin.ReadLine().Trim() |> int
printfn "suma=%d" (List.sum [ 1 .. n ])
Module Suma
Sub Main()
Dim n = Integer.Parse(Console.ReadLine().Trim())
Dim suma = 0
For i = 1 To n
suma += i
Next
Console.WriteLine("suma=" & suma)
End Sub
End Module
Qué reconocer: comparten System.Int32 y el mismo CLR, pero no comparten manera de pensar.
VB.NET viene del BASIC y escribe la receta paso a paso: declara el acumulador, lo modifica dentro del
For, lo imprime al final. F# viene de ML y escribe una expresión: construye la lista [1..n] y
la reduce con List.sum, sin acumulador y sin bucle, encadenando con |>. Ambos programas se
compilan a instrucciones parecidas; lo que cambia es dónde pone el autor su atención, en el cómo o
en el qué.
Representante del núcleo: C. El paradigma imperativo en estado puro: memoria, variables que cambian y control explícito del flujo.
#include <iostream>
int main() {
long long n = 0;
std::cin >> n;
long long suma = 0;
for (long long i = 1; i <= n; ++i) suma += i;
std::cout << "suma=" << suma << '\n';
}
#import <Foundation/Foundation.h>
int main(void) {
@autoreleasepool {
long n = 0;
scanf("%ld", &n);
long suma = 0;
for (long i = 1; i <= n; i++) suma += i;
printf("suma=%ld\n", suma);
}
return 0;
}
Qué reconocer: los dos son superconjuntos de C y, para un problema como este, ninguno saca sus
capacidades añadidas: no hay clases, no hay plantillas, no hay mensajes. Es el recordatorio de que un
lenguaje multiparadigma no te obliga a usar todos sus paradigmas; C++ podría resolverlo con
std::accumulate y Objective-C con una categoría sobre NSNumber, y eligen no hacerlo porque el
bucle es lo natural aquí. Elegir paradigma es una decisión de diseño, no una obligación del
compilador.
Representantes del núcleo: Go · Rust. Compilan a binario nativo y se toman en serio el coste de cada abstracción.
const std = @import("std");
pub fn main() !void {
var buf: [64]u8 = undefined;
const linea = (try std.io.getStdIn().reader().readUntilDelimiterOrEof(&buf, '\n')).?;
const n = try std.fmt.parseInt(u64, std.mem.trim(u8, linea, " \r"), 10);
var suma: u64 = 0;
var i: u64 = 1;
while (i <= n) : (i += 1) suma += i;
try std.io.getStdOut().writer().print("suma={d}\n", .{suma});
}
import std/strutils
let n = stdin.readLine().strip().parseInt()
var suma = 0
for i in 1 .. n:
suma += i
echo "suma=", suma
import std.stdio, std.string, std.conv, std.range, std.algorithm;
void main() {
const n = readln().strip().to!int;
writefln("suma=%d", iota(1, n + 1).sum);
}
Qué reconocer: Zig es deliberadamente el más pobre en paradigmas —no tiene clases, ni cierres con
captura implícita, ni sobrecarga de operadores— y esa pobreza es su tesis: una sola manera de hacer
las cosas para que leer el código baste para saber qué máquina se ejecuta. Nim escribe el mismo
bucle con aspecto de Python. D elige el otro extremo dentro de la misma familia: iota(1, n+1).sum
es una tubería declarativa sobre rangos perezosos, sin bucle visible, y aun así compila al mismo
binario nativo. Rendimiento y paradigma son ejes independientes.
Representante del núcleo: SQL. Se describe qué se quiere, no cómo recorrerlo paso a paso.
:- initialization(main, main).
% No hay bucle ni acumulador: dos reglas que definen qué es la suma hasta N.
suma(0, 0).
suma(N, S) :- N > 0, M is N - 1, suma(M, S0), S is S0 + N.
main :-
read_line_to_string(user_input, Linea),
number_string(N, Linea),
suma(N, S),
format("suma=~d~n", [S]).
// Datalog no tiene E/S ni bucles: los sumandos entran como hechos y la suma
// se obtiene con la agregación de Soufflé, que es una consulta, no un recorrido.
.decl sumando(x:number)
sumando(1). sumando(2). sumando(3). sumando(4). sumando(5).
.decl suma(s:number)
suma(s) :- s = sum x : { sumando(x) }.
Qué reconocer: Prolog no ofrece bucle porque no tiene nada que iterar: define la relación
suma(N, S) con dos cláusulas —el caso base y el caso general— y deja que el motor de resolución
encuentre el valor. S is S0 + N no asigna: liga S una sola vez con el resultado. Datalog va
más lejos y renuncia incluso a la recursión aritmética libre; el programa no dice en qué orden se
calcula nada. Este es el contraste más fuerte de toda la página: si en Java o en C quitas el bucle no
queda programa, y aquí el bucle nunca existió. Eso es cambiar de paradigma, no de sintaxis.
Veinte lenguajes, un solo problema de cuatro líneas, y cuatro maneras irreducibles de plantearlo: acumular en un bucle, plegar una secuencia, agregar un vector y declarar una relación. Ninguna es más correcta; cada una hace fácil un tipo de cambio y difícil otro. Reconocer cuál estás usando —y saber que podías haber usado otra— es lo que esta parte del curso te va a pedir en cada clase.