Agregar una función a un lenguaje de programación [cerrado]


55

Su tarea es agregar una característica a un lenguaje de programación, ya sea implementando una biblioteca muy inteligente o procesando el texto de entrada y / o ajustando el proceso de compilación.

Ideas:

  • Agregue intercalación de presentaciones de estilo PHP a C (por ejemplo <?c printf("Hello,"); ?> world!).
  • Agregue un operador de fusión nulo a uno de esos lenguajes que no sea C #.
  • Agregar macros a PHP.
  • Añadir gotoa JavaScript.
  • Agregue la coincidencia de patrones al lenguaje X
  • Agregue compatibilidad con espacios de nombres a un idioma que no lo tenga.
  • Haz que C parezca PHP.
  • Haz que Haskell se parezca a Pascal.
  • ... (siéntase libre de publicar ideas en la sección de comentarios)

Reglas:

  • Trae algo a la mesa. No solo diga "Template Haskell" para agregar servicios de metaprogramación a Haskell. Esto no es StackOverflow.
  • La implementación completa debe caber en una pantalla completa (sin contar el ejemplo).
  • No aloje código en un sitio externo específicamente para esta tarea.
  • La característica más impresionante o sorprendente gana.

No se preocupe por implementar la función 100% correctamente. ¡Lejos de ahi! El principal desafío es descubrir lo que quiere hacer y cortar brutalmente los detalles hasta que su empresa planificada sea factible.

Ejemplo:

Agregue un operador lambda al lenguaje de programación C.

Acercamiento inicial:

Bien, sé que me gustaría usar libgc para que mis lambdas resuelvan los problemas de funarg hacia arriba y hacia abajo. Supongo que lo primero que tendría que hacer es escribir / encontrar un analizador para el lenguaje de programación C, luego necesitaría aprender todo sobre el sistema de tipos de C. Tendría que descubrir cómo darle sentido en lo que respecta a los tipos. ¿Tendría que implementar la inferencia de tipos, o simplemente debería requerir que el parámetro formal se escriba como se indica? ¿Qué pasa con todas esas características locas en CI que aún no conocemos?

Está bastante claro que implementar lambda en C correctamente sería una tarea enorme. ¡Olvídate de la corrección! Simplifica, simplifica.

Mejor:

Atornille funargs hacia arriba, ¿quién los necesita? Podría hacer algo complicado con las funciones anidadas de GNU C y las expresiones de declaración . Quería mostrar una sorprendente transformación sintáctica en C con un código escueto y hacky, pero ni siquiera necesitaré un analizador para esto. Eso puede esperar para otro día.

Resultado (requiere GCC):

#include <stdio.h>
#include <stdlib.h>

#define lambda(d,e)({d;typeof(e)f(d){return(e);};f;})

#define map(F,A)({typeof(F)f=(F);typeof(*(A))*a=(A);({int i,l=((int*)(a))[-1]; \
typeof(f(*a))*r=(void*)((char*)malloc(sizeof(int)+l*sizeof(*r))+sizeof(int));  \
((int*)r)[-1]=l;for(i=0;i<l;i++)r[i]=f(a[i]);r;});})

#define convert_to(T) lambda(T x, x)
#define print(T, fmt) lambda(T x, printf(fmt "\n", x))

int main(void)
{
    int *array = 1 + (int[]){10, 1,2,3,4,5,6,7,8,9,10};
    map(print(int, "%d"), array);

    double *array2 = map(lambda(int x, (double)x * 0.5), array);
    map(print(double, "%.1f"), array2);

    long *array3 = map(convert_to(long), array2);
    map(print(long, "%ld"), array3);

    long product = 1;
    map(lambda(int x, product *= x), array);
    printf("product: %ld\n", product);

    return 0;
}

Eso fue fácil, ¿no? Incluso agregué una mapmacro para que sea útil y bonita.


10
Creo que Ken Thompson nos tiene a todos vencidos : 0 bytes de código.
dmckee

44
No quiero crear una respuesta completa, pero he agregado clases a GNU C , en caso de que alguien esté interesado.
Richard J. Ross III

3
No estoy seguro si esto califica, pero he escrito un ejemplo de continuaciones en C . Sin embargo, un poco más que un screenfull.
luser droog

1
Mi agradecimiento a quien resucitó esta pregunta; Tengo una excelente idea para mi presentación.
Jonathan Van Matre

2
Agrega una lambda a la C ... oye, no me mires así.
Leushenko

Respuestas:


27

Sintaxis de OOP en Haskell

import Prelude hiding ((.))
a . b = b a

Los objetos pueden tener propiedades:

[1,5,3,2].length -- 4
[1,5,3,2].maximum -- 5
'a'.succ -- 'b'

... y métodos:

"Hello world!".take(5) -- "Hello"
"Hello world!".splitAt(2) -- ("He","llo world!")
"Hello world!".map(toUpper) -- "HELLO WORLD!"

2
En algún lugar vi a este operador escrito como &y definido así (&) = flip ($).
swish

66
@swish no lo utilicé &porque es el operador unario 'dirección de' (la implementación de punteros en Haskell se deja como un ejercicio para el lector).
lortabac

1
@swish puede guardar un personaje (y un ciclo de cerebro) mediante el uso deflip id
Sean D

24

goto en JavaScript?

Mi primer pensamiento fue un enfoque funcional : agregar un parámetro a la función para indicar dónde debería comenzar la ejecución, usando eso con una switchdeclaración y un bucle externo que llama repetidamente a la función en su propio valor de retorno . Desafortunadamente, eso impediría el uso de variables locales, ya que perderían sus valores con cada goto.

Podría usar una withdeclaración y mover todas las declaraciones de variables al comienzo de la función, pero tenía que haber una mejor manera. Eventualmente se me ocurrió usar el manejo de excepciones de JavaScript . De hecho, Joel Spolsky dijo: "Considero que las excepciones no son mejores que" ir a ... " , obviamente un ajuste perfecto.

La idea era poner un bucle infinito dentro de una función, terminado solo por una returndeclaración o una excepción no detectada. Todos los gotos, tratados como excepciones, quedarían atrapados dentro del ciclo para evitar su terminación. Aquí está el resultado de ese enfoque:

function rewriteGoTo(func) {
    var code = '(';
    code += func.toString()
        .replace(/^\s*(\w+)\s*:/gm, 'case "$1":')
        .replace('{', '{ var $_label = ""; function goTo(label) { $_label = label; throw goTo; } while(true) try { { switch($_label) { case "": ');
    code += '} return; } catch($_e) { if($_e !== goTo) throw $_e; } })';
    return code;
}

Puede usarlo así, incluso en modo estricto ES5 , excepto en Internet Explorer ( demo ):

var test = eval(rewriteGoTo(function(before) {
    var i = 1;
    again: print(before + i);
    i = i + 1;
    if(i <= 10) goTo('again');
}));

[Internet Explorer, por alguna razón, no evalúa el código de una función anónima, por lo que habría que darle un nombre a la función (antes de reescribirla) y llamarla con ese nombre. Por supuesto, eso probablemente rompería las reglas del modo estricto.]

Esto no permite saltar a una declaración ubicada dentro de un bloque (hasta que tales construcciones como el dispositivo de Duff se vuelvan legales), pero podemos ocuparnos de eso (otra función reescrita autoejecutable), ¿verdad?


1
¡Dulce! Buen trabajo manteniéndolo simple. Una curiosidad interesante: si gotose implementara completamente en JavaScript (a donde podría usar gotopara saltar fuera de cualquier alcance, incluso una función ), implicaría soporte para las continuaciones.
Joey Adams

22

#define en Java

Pensé que sería divertido implementar macros en Java.

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * defines the use of #define. Usage:
 *
 * #def toReplaceCanHaveNoSpaces replacement can have extra spaces
 *
 * must be at the beginning of the line (excluding starting spaces or tabs)
 * 
 * @author Quincunx
 */
public class Define {

    public static void main(String[] args) {
        if (args.length != 1) {
            err("Please provide exactly 1 argument");
        }
        File source = new File(args[0]);
        if (!source.exists()) {
            err("Supplied filepath does not point to an existing file");
        }
        if (!getExtension(args[0]).equalsIgnoreCase(".java")) {
            err("Supplied file is not of type .java");
        }
        ArrayList<String> sourceData = new ArrayList<>();
        ArrayList<String[]> replacements = new ArrayList<>();
        try {
            BufferedReader read = new BufferedReader(new FileReader(source));
            String data;
            while ((data = read.readLine()) != null) {
                sourceData.add(data);
            }
            read.close();
        } catch (IOException ex) {
            Logger.getLogger(Define.class.getName()).log(Level.SEVERE, null, ex);
        }
        for (int index = 0; index < sourceData.size(); index++) {
            String line = sourceData.get(index);
            line = line.replaceAll("\t", "    ");
            for (String[] e : replacements) {
                line = line.replace(e[0], e[1]);
            }

            if (line.trim().charAt(0) != '#') {
                sourceData.set(index, line);
                continue;
            }
            while (line.charAt(0) != '#') {
                line = line.substring(1);
            }
            int indexOf = line.indexOf(" ");
            String type = line.substring(1, indexOf);

            switch (type) {
                case "def":
                case "define":
                    String toReplace = line.substring(indexOf + 1, line.indexOf(" ", indexOf + 1));
                    replacements.add(new String[]{toReplace, line.substring(line.indexOf(":") + 1)});
                    break;
                default:
                    err("The source code contains a # in which there is no correct type");
            }
        }

        try {
            BufferedWriter write = new BufferedWriter(new FileWriter(source));
            for (String s : sourceData) {
                write.write(s);
                write.newLine();
            }
            write.close();
        } catch (IOException ex) {
            Logger.getLogger(Define.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public static void err(String message) {
        System.err.println(message);
        System.exit(1);
    }

    public static String getExtension(String filePath) {
        return filePath.substring(filePath.lastIndexOf("."));
    }

}

Uso de muestra (se convierte en código publicado anteriormente; hagámoslo raro):

#def @ o
#def ~ a
#def $ i
#def ` e
#d`f % m
#d`f ! {
#d`f & d
#&`f _ }
#&`f 2 (
#&`f 7 )
#&`f $%p@rt$@. $%p@rt j~v~.$@.
#&`f $%p@rtu. $%p@rt j~v~.ut$l.
#&`f ps publ$c st~t$c
#&`f Str Str$ng

$%p@rt$@.Buff`r`&R`~&`r;
$%p@rt$@.Buff`r`&Wr$t`r;
$%p@rt$@.F$l`;
$%p@rt$@.F$l`R`~&`r;
$%p@rt$@.F$l`Wr$t`r;
$%p@rt$@.IOExc`pt$@n;
$%p@rtu.Arr~yL$st;
$%p@rtu.l@gg$ng.L`v`l;
$%p@rtu.l@gg$ng.L@gg`r;

#d`f L$st Arr~yL$st
#d`f l@g; L@gg`r.g`tL@gg`r2D`f$n`.cl~ss.g`tN~m`277.l@g2L`v`l.SEVERE, null, `x7;    

publ$c cl~ss D`f$n` !

    ps v@$d %ain2Str[] ~rgs7!
        $f 2~rgs.l`ngth != 17 !
            `rr2"Pl`~s` pr@v$&` `x~ctly 1 ~rgu%`nt"7;
        _
        F$l` squrc` = n`w F$l`2~rgs[0]7;
        $f 2!sourc`.`x$sts277 !
            `rr2"Suppli`& f$l`p~th &@`s n@t p@int t@ ~n `x$st$ng f$l`"7;
        _
        $f 2!g`tExt`ns$@n2~rgs[0]7.`qu~lsIgn@r`C~s`2".j~v~"77 !
            `rr2"Suppl$`& f$l` $s n@t @f typ` .j~v~"7;
        _
        L$st<Str> s@urceDat~ = n`w List<>27;
        L$st<Str[]> repl~cem`nts = n`w L$st<>27;
        try !
            Buff`r`&R`a&`r r`a& = new Buff`redRe~&`r2n`w F$l`R`~&`r2s@urc`77;
            Str &~t~;
            wh$l` 22&~t~ = r`~&.r`~&L$n`277 != null7 !
                s@urc`D~t~.~&&2&ata7;
            _
            re~&.cl@se27;
        _ c~tch 2IOExc`ption ex7 !
            log;
        _
        f@r 2$nt $n&`x = 0; $ndex < s@urc`D~t~.s$z`27; $nd`x++7 !
            Str l$n` = s@urc`D~ta.get2index7;
            line = line.r`pl~c`All2"\t", "    "7;
            for 2Str[] ` : r`pl~c`%`nts7 {
                line = line.r`pl~c`2`[0], e[1]7;
            _

            if 2l$ne.tr$%27.ch~rAt207 != '#'7 !
                sourc`D~t~.s`t2$n&`x, l$n`7;
                c@nt$nu`;
            _
            wh$l` 2line.ch~rAt207 != '#'7 !
                l$ne = l$ne.substr$ng217;
            _
            $nt in&`xOf = line.$n&`xOf2" "7;
            Str typ` = line.substring21, indexOf7;

            sw$tch 2type7 !
                c~s` "&`f":
                c~s` "def$n`":
                    str t@R`pl~c` = line.substring2indexOf + 1, line.indexOf2" ", indexOf + 177;
                    r`pl~c`%`nts.~&&2n`w s\Str[]!t@R`place, line.substring2line.indexOf2":"7 + 17_7;
                    br`~k;
                def~ult:
                    err2"Th` s@urc` c@&` c@nt~$ns ~ # $n wh$ch th`r` i$s n@ c@rr`ct typ`"7;
            _
        _

        try !
            Buff`r`&Wr$ter wr$te = new BufferedWriter2new F$l1Wr$t1r2s@urc177;
            for 2Str s : s@urceData7 !
                wr$te.write2s7;
                wr$te.n`wLin`27;
            _
            wr$t`.cl@s`27;
        _ c~tch 2IOExc`pt$@n `x7 !
            l@g;
        _
    _

    ps v@$& `rr2Str m`ss~g`7 !
        Syst`%.`rr.pr$ntln2message7;
        Syst`%.`x$t217;
    _

    ps Str g`tExt`nsi@n2Str fileP~th7 !
        r`turn f$lePath.substr$ng2f$l`P~th.l~stInd`xOf2"."77;
    _

_

77
Me estaba desplazando a través del segundo bloque, y mi único pensamiento fue "... por la madriguera del conejo".
Soham Chowdhury

18

Foreach en C

Iterar matrices (funciona para matrices estáticas, no unas, recibidas a través del puntero)

//syntactic beauty
#define in ,    

//syntactic beauty's helper macro
#define foreach(a) _foreach(a)

//the real foreach macro
#define _foreach(e,arr)\
typeof (&arr[0]) e;\
for (e=&arr[0];e!=&arr[sizeof(arr)/sizeof(arr[0])];e++)

Para probarlo:

int int_arr[3]={10,20,30};    
char *strings[]={"Hello","World","Foreach","Test"};

foreach (num in int_arr) {
        printf ("num=%d\n",*num);
}

foreach (str in strings) {
        printf ("str=%s\n",*str);
}

resultado:

num=10
num=20
num=30
str=Hello
str=World
str=Foreach
str=Test

17

Propiedades en C

Tomasz Wegrzanowski implementó propiedades en el plano C, segfaulando intencionalmente el programa cuando se accede a la propiedad.

Un objeto con una "propiedad" se configura creando un structque cruza varias páginas, asegurando que la dirección de memoria de la propiedad se encuentre en una página diferente de los miembros de datos reales. La página de la propiedad está marcada como sin acceso, lo que garantiza que intentar acceder a la propiedad causará un defecto. Luego, un manejador de fallas determina qué acceso a la propiedad causó la falla predeterminada y llama a la función apropiada para calcular el valor de la propiedad, que se almacena en la dirección de memoria de la propiedad.

El controlador de fallas también marca la página de datos como de solo lectura para garantizar que el valor calculado permanezca consistente; la próxima vez que intente escribir en un miembro de datos, eso desencadena un defecto de seguridad, cuyo controlador establece la página de datos como lectura-escritura y la página de propiedades como no acceso (lo que indica que necesita ser recalculado).


15

Procedente calculado en Common Lisp

Inicialmente implementé venir de. Pero eso no fue lo suficientemente bueno.

Inspirado por el goto computarizado, decidí implementar el origen computarizado.

(defmacro computed-come-from-tagbody (&rest statements)
  (let ((has-comp-come-from nil)
        (comp-come-from-var nil)
        (start-tag (gensym))
        (end-tag (gensym)))

    (let ((current-tag start-tag)
          (come-froms (make-hash-table :test #'eq)))

      (let ((clauses '()))
        (loop for statement in statements do
             (if (symbolp statement)
                 (setf current-tag statement))

             (cond
               ((and (consp statement)
                     (eql 'come-from (car statement)))

                (setf has-comp-come-from t)
                (setf (gethash (cadr statement) come-froms) current-tag))
               (t (push statement clauses))))


        (if (not has-comp-come-from)
            `(tagbody ,@(reverse clauses))
            (let ((res '())
                  (current-tag start-tag))
              (loop for clause in (reverse clauses) do
                   (cond
                     ((symbolp clause)
                      (push clause res)
                      (setf current-tag clause)
                      ;; check all vars for jumps
                      (push
                       `(progn ,@(loop for k being the hash-key of come-froms
                                    for v being the hash-value of come-froms collect
                                      `(when (eql ,k ,current-tag)
                                         (go ,v))))
                       res))
                     (t (push clause res))))
              `(macrolet ((come-from (idx)
                            (declare (ignore idx))
                            (error "Come-from cannot be used with another form.")))
                 (tagbody ,@(reverse res)))))))))

Ejemplos de uso

(come-from x) ; whenever we're at the top of a labeled block and the value of x is equal to the label, jump back to this point.

Para cada declaración de origen en el cuerpo de etiqueta, comprobará en cada etiqueta si la variable de origen es igual a la etiqueta actual y, de ser así, saltará a la declaración de origen correspondiente.

Greeter

(let ((x :repeat)
      (y :exit))
   (computed-come-from-tagbody
      :loop              ;; when x == :loop jump to :loop.  when y == :loop jump to :exit
      (come-from x)
      (format t "What is your name? ")
      (let ((name (read-line)))
         (terpri)
         (format t "Hello ~a~%" name)
         (print (string= name "exit"))
         (when (string= name "exit")
             (setf x nil
                   y :repeat)))
       :repeat           ;; when x = :repeat jump to loop, when y = :repeat jump to exit
       :exit             ;; when x = :exit jump to loop, when y = :exit jump to exit
       (come-from y)))

FizzBuzz

(let ((i 0)
      (x nil)
      (y nil))
   (computed-come-from-tagbody
       :loop
       (come-from x)
       (cond
         ((> i 100)  (setf x nil
                           y :end-iteration)) 
         (t (or (and (zerop (mod i 3)) (zerop (mod i 5)) (print "FizzBuzz"))
                (and (zerop (mod i 3)) (print "Fizz"))
                (and (zerop (mod i 5)) (print "Buzz"))
                (print i))  
            (incf i)
            (setf x :end-iteration)))
       :end-iteration
       :end
       (come-from y)
       (print "done")))

14

"Autocadenas" en Ruby

El código es bastante simple:

def self.method_missing *a; a.join ' '; end

Ahora puedes hacer

print This is an automatic string #=> This is an automatic string
print hooray #=> hooray

x = code golf
print This is + ' ' + x + '!' #=> This is code golf!


13

Agregar macros a PHP

Podemos usar el preprocesador C para esta tarea.

Un script php:

<?php

#define ERROR(str) trigger_error(#str, E_USER_ERROR)

function test() {
        ERROR(Oops);
}

Tubería aunque cpp:

cpp < test.php

Resultado:

<?php

function test() {
 trigger_error("Oops", E_USER_ERROR);
}

¿No romperá eso con las características de PHP que no existen en C? Como los heredocs. Afair, el C PP estaba muy ligado a la gramática de C.
Joey

1
Creo que el preprocesador solo elimina la entrada sin tratar de darle sentido. <<<HEREDOCSin embargo, An no es más que 3 desplazamiento inferior o izquierdo y un identificador :-) Sin embargo, esto hará una macro sustitución en cadenas heredoc.
Arnaud Le Blanc

El preprocesador C agrega basura adicional a la salida, por lo que su ejemplo no funcionaría como se esperaba
cobarde anónimo

1
A quien lo grep -v ^#arreglaría. Supongo que esto es suficiente para esta pregunta :-)
Arnaud Le Blanc

10

Guardias de coincidencia de patrones en Python

def pattern_match(n, s="__fns"):
 s=n+s;g=globals()
 def m(f):
  def a(*r):
   for f in g[s]:
    if reduce(lambda c,t:c and eval(t[1:],{},dict(zip(f.func_code.co_varnames,r))),filter(lambda x:x and x[0]is"|",map(lambda x:x.strip(),f.func_doc.split("\n")))): return f(*r)
  g[n]=a;g[s]=(g.get(s)or[])+[f]
  return a
 return m

El cuerpo de la función viene en 288 caracteres.

Guardias de coincidencia de patrones le permiten utilizar funciones completamente diferentes según los valores de los argumentos. Aunque se puede emular fácilmente con una serie de ifdeclaraciones, los protectores de coincidencia de patrones pueden ayudar a separar secciones de código, y es una gran excusa para hacer una metaprogramación loca.

pattern_matches un decorador que crea una nueva función que implementa guardias de coincidencia de patrones . Las condiciones para cada "subfunción" dada en cada cadena de documentos en líneas que comienzan con una tubería ( |). Si todas las condiciones se evalúan de verdad, se ejecuta esa versión de la función. Las funciones se prueban en orden hasta que se encuentra una coincidencia. De lo contrario, Nonese devuelve.

Un ejemplo ayudará a aclarar:

@pattern_match("test1")
def test1_a(a, b, c):
    """
    This guard tests if a and c are positive

    | a > 0
    | c > 0
    """
    return a + b + c

@pattern_match("test1")
def test1_b(a, b, c):
    """
    This pattern only ensures b is positive

    | b > 0
    """
    return b + c

@pattern_match("test1")
def test1_c(a, b, c):
    """
    Final catchall

    | True
    """
    return 0


print test1(1,2,3) # (a) >>> 6
print test1(1,2,0) # (b) >>> 2
print test1(1,0,0) # (c) >>> 0
print test1(0,0,1) # (b) >>> 1

En Haskell, esto se llama guardias , no coincidencia de patrones. En Haskell, la coincidencia de patrones le permite decir f [a,b,c] = ..., que no solo prueba el argumento contra un predicado, sino que une las variables respectivas en una coincidencia exitosa. Sin embargo, esto sigue siendo bastante bueno.
Joey Adams

D'oy! ¡Gracias por corregir eso! También estaba pensando en Haskell, centrándome específicamente en definir una función con dos predicados diferentes (es decir, f (x:xs) = ...y f [] = ...). De alguna manera enrevié a los guardias allí, pero de ahí tomé el |.
zbanks

Este no es un desafío de código de golf; ¡puedes ser más detallado (y legible) si quieres! :)
ReyCharles


7

Operadores personalizados en Lua

Pogs abusó hábilmente de la sobrecarga de operadores en Lua para permitir la definición de operadores infix personalizados. He expandido esto para admitir la sección del operador (aplicando parcialmente un operador con cualquier operando) y llamando al objeto resultante como si fuera una función.

---- implementation
function infix(f)
  local function g(self, x)
    return f(self[1] or x, self[2] or x)
  end

  local mt   = { __sub = g, __call = g }
  local self = {}
  return setmetatable(self,
           { __sub = function (lhs,rhs)
                       return rhs == self and setmetatable({ lhs, nil }, mt)
                                           or setmetatable({ nil, rhs }, mt)
                     end })
end

---- testing
local eq   = infix(function (a, b) return a == b end)
local ge   = infix(function (a, b) return a >= b end)

local comp = infix(function (a, b) return a < b and -1
                                       or a > b and  1
                                       or            0 end)

function filter(pred, xs)
  local res = {}
  for i=1,#xs do
    if pred(xs[i]) then table.insert(res, xs[i]) end
  end
  return res
end

print(1  -eq-  1)                                      --> true
print(1 -comp- 0)                                      --> 1
print((4 -ge)(1))                                      --> true
print(table.unpack(filter(ge- 0, {1,-4,3,2,-8,0})))    --> 1   3   2   0

7

Cadenas multilínea en javascript

En esta sintaxis elaborada para cadenas multilínea, cada cadena multilínea irá precedida de (function(){/*una nueva línea, seguida de una nueva línea y */}+'').split('\n').slice(1,-1).join('\n').

Con esta increíble e intuitiva sintaxis, finalmente podemos usar cadenas multilínea:

var string = (function(){/*
THIS IS A MULTILINE STRING
HOORAY!!!
*/}+'').split('\n').slice(1,-1).join('\n');

console.log(string) // THIS IS A MULTILINE STRING
                    // HOORAY!!!

Para las personas a las que no les gusta nuestra sintaxis simple, tenemos un compilador de nuestro nuevo lenguaje fabuloso:

function compile(code)
{
    return code.replace("#{", "(function(){/*").replace("}#", "*/}+'').split('\n').slice(1,-1).join('\n')")
}

el mismo ejemplo, en la versión en lenguaje compilado:

var string = #{
THIS IS A MULTILINE STRING
HOORAY!!!
}#;
console.log(string) // THIS IS A MULTILINE STRING
                    // HOORAY!!!

1
Por alguna razón no puedo poner */mis cadenas multilínea. ¡Esto es súper molesto cuando se incluyen expresiones regulares en las cadenas!
FireFly

@FireFly En realidad, creo que esto todavía funciona. Sin embargo, el resaltado de syntex se vuelve extraño.
orgulloso Haskeller

6

Lista divisible en C # (como Python)

Siempre disfruté de la notación de corte de Python y desearía que estuviera disponible en C #

Uso:

SliceList<int> list = new SliceList<int>() { 5, 6, 2, 3, 1, 6 };
var a = list["-1"];     // Grab the last element (6)
var b = list["-2:"];    // Grab the last two elements (1,6)
var c = list[":-2"];    // Grab all but the last two elements (5,6,2,3)
var d = list["::-1"];   // Reverse the list (6,1,3,2,6,5)
var e = list["::2"];    // Grab every second item (5,2,1)

Código, lejos de prueba de error:

public class SliceList<T> : List<T>
{
    public object this[string slice]
    {
        get
        {
            if (string.IsNullOrWhiteSpace(slice))
                return this.ToList();
            int[] values = { 0, Count, 1 };
            string[] data = slice.Split(':');
            for(int i = 0; i < data.Length; i++)
            {
                if (string.IsNullOrEmpty(data[i])) continue;
                int value;
                int.TryParse(data[i], out value);
                if(value < 0 && i < 2)
                    value += Count;
                values[i] = value;
            }
            if (data.Length == 1)
                return this[values[0]];
            int start = Math.Min(values[0], values[1]);
            int stop = Math.Max(values[0], values[1]);
            int step = values[2];
            int sign = Math.Sign(step);
            if (sign < 0)
            {
                var temp = start;
                start = stop-1;
                stop = temp-1;
            }

            SliceList<T> newList = new SliceList<T>();
            for (int i = start; i != stop; i += step)
                newList.Add(this[i]);

            return newList;
        }
    }
}

Solicité el corte hace mucho tiempo para que se incluyera en .NET, todavía se ignora :(
Ray

6

Hacer C más simple

Este código le permite escribir programas en C que se parecen un poco más a un lenguaje de script. Cuenta con palabras clave como 'var', 'is', 'string', 'plus', 'equal' y varias otras. Funciona a través de muchas declaraciones de definición.

// pretty.c

#include<stdio.h>

#define function int
#define var int
#define is =
#define then {
#define do {
#define does {
#define end }
#define equal ==
#define notequal !=
#define greater >
#define less <
#define greaterequal >=
#define lessequal <=
#define display printf
#define otherwise }else{
#define increase ++
#define decrease --
#define plus +
#define minus -
#define times *
#define divide /
#define character char
#define string char*
#define integer int

Esto le permite escribir código como:

/*
Preprocessor abuse, Yay!
*/

#include "pretty.c"

function main() does
    var myVar is 1;
    if(myVar greater 2)then
        display("Yep\n");
    otherwise
        display("Nope\n");
    end

    for(var i is 0; i less 10; i increase)do
        display("Loop: %d\n", i);
    end

    string myString = "Hello";
    display(myString);
end

Lo anterior se expande a:

int main() {
    int myVar = 1;
    if(myVar > 2){
        printf("Yep\n");
    }else{
        printf("Nope\n");
    }

    for(int i = 0; i < 10; i ++){
        printf("Loop: %d\n", i);
    }

    char* myString = "Hello";
    printf(myString);
}

Probablemente no sea demasiado útil, pero me pareció bastante interesante que esencialmente pudieras crear un lenguaje de programación completo a través de un montón de #defines.


Eso parece una combinación de Javascript / Ruby ...
Beta Decay

Prácticamente no hay límite superior para esto: con #defines lo suficientemente complejos , incluso puede darle a su idioma cosas como manejo de excepciones y recolección de basura mientras mantiene la capa C fundamental debajo.
Leushenko

5

Tcl

Tcl no tiene más do ... whileo do ... untilmenos ...

proc do {body op expr} {
    uplevel 1 $body
    switch -exact -- $op {
        while {
            while {[uplevel 1 [list expr $expr]} {
                uplevel 1 $body
            }
        }
        until {
            while {![uplevel 1 [list expr $expr]} {
                 uplevel 1 $body
            }
        }
    }
}

Ejemplo:

do {
    puts -nonewline "Are you sure? "
    flush stdout
    set result [gets stdin]
} while {[string is boolean -strict $result]}

uplevel ejecuta un script en el alcance de los llamantes.


5

Ir a PostScript

Mi primer pensamiento fue que tendría que perder el tiempo con la pila de ejecutivos, por lo que este inicio falso desentierra el operador de continuación para detener desde ghostscript (o xpost).

/_stopped_mark
{countexecstack array execstack dup length 2 sub get}
stopped pop def 

Pero, es más simple que eso. Debido a que la posición del archivo es la misma para todos los duplicados del identificador de archivo ( setfilepositionconsume su argumento, por lo que esta es la única semántica útil para esa función).

/LABELS 10 dict def 

/: { % n :  define label
    LABELS exch currentfile fileposition put 
} def 

/goto { % goto label
    currentfile exch LABELS exch get setfileposition
} def 

/x 0 def 

/here :
    /x x 1 add def 

    x 5 ne {
        /here goto
    } if

x =

Se imprime 5.

Hay algunas limitaciones con lo anterior. El salto no es inmediato, sino que ocurre cuando el cuerpo del if vuelve al nivel superior y el intérprete vuelve a leer del archivo (en lugar de leer del conjunto que contiene el cuerpo del if). En ese punto, el archivo ha sido reposicionado y el 'goto' tiene efecto.


Y son solo definiciones en un diccionario, por lo que puede usar casi cualquier tipo para las etiquetas.
luser droog

También puede hacer saltos absolutos con currentfile <pos> setfileposition, contando bytes desde el inicio del archivo.
luser droog

4

Symbol#to_proc con argumentos en Ruby

Symbol#to_proces probablemente uno de mis trucos favoritos para escribir código Ruby realmente sucinto. Supongamos que tienes

nums = [1, 2, 3, 4]
text = %w(this is a test)

y desea convertir el contenido de numsy texta flotantes y palabras mayúsculas, respectivamente. Symbol#to_procle permite acortar código como este:

nums.map { |num| num.to_f }
text.map { |word| word.upcase }

a esto:

nums.map(&:to_f)
text.map(&:upcase)

¡Increíble! Pero lo que si queremos aumentar todos los elementos de numsla ipotencia XX, o reemplazar todas las apariciones de sla *de text? ¿Hay alguna forma de acortar código como este?

nums.map { |num| num ** 1i }
nums.map { |word| word.gsub('s', '*') }

Por desgracia, no hay una manera fácil de pasar argumentos cuando se usa Symbol#to_proc. Lo he visto de varias maneras, pero probablemente dos de las más inteligentes y utilizables implican parches de mono en la Symbolclase [ 1 , 2 ]. Ilustraré la primera forma a continuación.

class Symbol
  def with(*args, &block)
    ->(caller, *rest) { caller.send(self, *rest, *args, &block) }
  end
end

Ahora puedes hacer cosas como:

nums.map(&:**.with(1i))
text.map(&:gsub.with('s', '*'))
nums.take_while(&:<.with(3))
text.delete_if(&:[].with('is'))

3

JavaScript foreach

var arr = ["Seattle", "WA", "New York", "NY", "Chicago", "IL"];

function foreach(fn, arr) {
  var s = fn.toString();
  var args = s.substring(s.indexOf('(')+1,s.indexOf(')')).split(",");
  var argsLen = args.length;
  var len = arr.length;
  for (var i = 0; i < len; i+=argsLen) {
    var part = arr.slice(i, i+argsLen);
    fn.apply(undefined, part);
  }
}

foreach (function(city, state) {
  console.log(city + ', ' + state);
}, arr);

Salida

Seattle, WA
New York, NY
Chicago, IL

Sintaxis alternativa, más como Tcl.

// Tcl's foreach loop for javascript.
// Keys in loop are prefixed with "this".
function tclForeach(keys, values, fn) {
  var obj={}, klen=keys.length, vlen=values.length, j, i;
  for (i=0, klen=keys.length; i < klen; i++) obj[keys[i]]=null;
  for(i = 0; i < vlen; i+=klen) {
    for(j=klen; j--;) obj[keys[j]] = values[i+j];
    fn.apply(obj);
  }
}

tclForeach(["city","state"], arr, function() {
  console.log(this.city + ', ' + this.state);
});

Esto no es un foreach simple, pero es más interesante. Inspecciona la lista de argumentos de la función de consumo. Podrías ir más lejos con este truco y hacer cosas realmente geniales.
Joey Adams

1
Iba por un foreach estilo Tcl. He agregado un enfoque ligeramente diferente que se parece más a Tcl.
wolfhammer

2

Gotos en Haskell

La idea básica es que los gotos se pueden simular parcialmente usando la última instrucción en doanotaciones. por ejemplo:

main = do
  loop:
  print 3
  goto loop

es equivalente a

main = do
  loop
loop = do
  print 3
  loop

Debido a que la ejecución saltará a la última declaración, es óptimo expresar gotos.

porque de la forma en que se hace, los gotos solo saltan cuando están dodirectamente en el bloque de una definición de nivel superior. en realidad es "llamar a x e ignorar el resto de las declaraciones vistas léxicamente " en lugar de "todas las x e ignorar el resto de las declaraciones", como un verdadero goto.

El mayor problema es que cuando no hay forma de dejar la ejecución en medio de una acción de E / S, incluso returnno lo hace; returnno hace nada cuando no es la última declaración.

esto supera esto al capturar el resto de las declaraciones en otro dobloque.

goto loop
print 3

se convierte

const loop $ do
print 3

la print 3declaración es capturada por el dobloque, por lo que se loopconvierte en la última declaración.

Esta transformación también admite variables presentes en el alcance de las acciones. Esto se hace recordando las variables que están dentro del alcance y pasándolas a las acciones. por ejemplo:

printer r = do
  loop:
  putStrLn r
  goto loop
  print "this isn't executed"

esto simplemente se traduce en:

printer r = do
  loop r
loop = do
  putStrLn r
  const (loop r) $ do
  print "this isn't executed"

algunas notas:

Además, return undefinedse agrega una declaración para garantizar que el dobloque de captura no esté vacío.

porque a veces hay ambigüedad de tipo en el dobloque de captura , en lugar de constusar asTypeOf, que es lo mismo constpero requiere que sus dos parámetros tengan el mismo tipo.

la implementación real (en javascript):

function makeGoto(code)
{
    var vars = [] // the known variables

    // add the arguments to the functions to scope
    code.split('\n')[0].split('=')[0].split(' ').slice(1).forEach(function(varname){vars.push(varname)})
    return code.replace(/([ \t]*)([a-zA-Z]+):|([ \t]*)goto[ \t]+([a-zA-Z]+)|[ \t]+([a-zA-Z]+)[ \t]*<-/gm, function match(match, labelSpaces, label, gotoSpaces, goto, x)
        {
            if (label != undefined)
                return labelSpaces+label+" "+vars.join(' ')+"\n"+label+" "+vars.join(' ')+"=do ";
            else if(goto != undefined)
                return gotoSpaces+"asTypeOf("+goto+" "+vars.join(' ')+")$do\n"+gotoSpaces+"return undefined";
            else
            {
                vars.push(x);
                return match
            }
        })
}

un examen:

main = do
    putSrtLn "a"
    goto label
    putStrLn "b"
    label:
    putStrLn "c"

se convierte en:

main = do
    putStrLn "a"
    asTypeOf(label )$do
    return undefined
    putStrLn "b"
    label 
label =do 
    putStrLn "c"

salida:

a
c

Vale la pena aclarar que returnen Haskell es una función regular, y no está relacionada con la palabra clave en C / etc.
FireFly

1

Python Goto

goto.py

import sys, re
globals_ = globals()
def setglobals(g):
    global globals_
    globals_ = g
def goto(l):
    global globals_ 
    with open(sys.argv[0], "rb") as f:    
        data = f.read()
        data_ = data.split('\n')
    if isinstance(l, int):
        l-=1 if l > 0 else 0
    elif isinstance(l, str):
        r=re.search(r"^\s*(#{0}|(def|class)\s+{0})".format(l), data, re.MULTILINE)
        l=len(data_)-(data[r.start():].count("\n")) if r else len(data_)
    if 0 < l < len(data_) or 0 < (l*-1) <= len(data_):
        exec("".join(data_[l:]),globals_)
        sys.exit(1)

Uso

setglobals(globals()) #Set the globals to be used in exec to this file's globals (if imports or other variables are needed)
goto(8) #Goto line 8
goto(-8)#Goto 8th last line
goto("label")#Goto first occurrence of #label
goto("funcName")#Goto definition of funcName
goto("className")#Goto definition of className

Caso de prueba de muestra

import goto, sys
goto.goto(-1)
sys.exit(-1)

print "Asdf"

Ejemplo de salida de caso de prueba

Asdf

Solo un poco divertido con exec (). Puede generar un error de profundidad de recursión máxima si no se usa correctamente.


-2

// importa javascript sin usar específicamente la etiqueta de script en una página HTML

function i(u) {
  document.write("script src=\" + u + \"></script>");
}

i("http://www.mysite.com/myscript.js");

Es patético, sí, lo sé. Longitud: 99


@ user2509848: este hilo no está etiquetado con código golf.
Joey Adams

Lo que publicaste requiere una scriptetiqueta a su alrededor. Entonces, ¿dónde está exactamente la nueva característica?
manatwork

@JoeyAdams Vaya, lo siento.
Hosch250
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.