Hinzufügen eines Features zu einer Programmiersprache [geschlossen]


55

Ihre Aufgabe ist es, ein Feature in eine Programmiersprache zu integrieren, indem Sie entweder eine sehr clevere Bibliothek implementieren oder den eingegebenen Text verarbeiten und / oder den Kompilierungsprozess optimieren.

Ideen:

  • Fügen Sie C eine PHP-artige Präsentationsverschachtelung hinzu (zB <?c printf("Hello,"); ?> world!).
  • Fügen Sie einer der Sprachen, die nicht C # sind, einen Null-Vereinigungsoperator hinzu .
  • Fügen Sie PHP Makros hinzu.
  • Fügen Sie gotoJavaScript hinzu.
  • Füge die Musterübereinstimmung zu Sprache X hinzu.
  • Hinzufügen von Namespace-Unterstützung zu einer Sprache, in der dies nicht der Fall ist.
  • Stellen Sie sicher, dass C wie PHP aussieht.
  • Lass Haskell wie Pascal aussehen.
  • ... (zögern Sie nicht, Ideen im Kommentarbereich zu posten)

Regeln:

  • Bring etwas zum Tisch. Sagen Sie nicht einfach "Template Haskell", um Haskell Metaprogrammierfunktionen hinzuzufügen. Dies ist kein StackOverflow.
  • Die gesamte Implementierung sollte in einen Bildschirm passen (ohne das Beispiel zu zählen).
  • Hosten Sie Code nicht speziell für diese Aufgabe auf einer externen Site.
  • Das beeindruckendste oder überraschendste Feature gewinnt.

Machen Sie sich keine Sorgen, wenn Sie die Funktion zu 100% korrekt implementieren. Weit davon entfernt! Die größte Herausforderung besteht darin, herauszufinden, was Sie tun möchten, und Details bösartig auszuschneiden, bis Ihr geplantes Unternehmen durchführbar wird.

Beispiel:

Fügen Sie der Programmiersprache C einen Lambda-Operator hinzu .

Anfänglicher Ansatz:

Okay, ich weiß, dass ich libgc verwenden möchte, damit meine Lambdas die Aufwärts- und Abwärts-Funarg-Probleme lösen. Ich denke, das erste, was ich tun müsste, ist, einen Parser für die Programmiersprache C zu schreiben / zu finden. Dann müsste ich alles über das Typensystem von C lernen. Ich müsste herausfinden, wie ich es verstehen kann, wenn es um Typen geht. Muss ich eine Typinferenz implementieren oder muss ich einfach nur den formalen Parameter wie angegeben eingeben? Was ist mit all den verrückten Funktionen in CI, von denen ich noch nichts weiß?

Es ist ziemlich klar, dass die korrekte Implementierung von Lambda in C ein großes Unterfangen wäre. Vergiss die Korrektheit! Vereinfachen, vereinfachen.

Besser:

Wer braucht sie? Ich könnte in der Lage sein, mit den verschachtelten Funktionen und Anweisungsausdrücken von GNU C etwas heikles zu machen . Ich wollte eine erstaunliche syntaktische Transformation in C mit knappem, hackigem Code demonstrieren, aber ich brauche dafür nicht einmal einen Parser. Das kann auf einen anderen Tag warten.

Ergebnis (erfordert 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;
}

Das war doch einfach, oder? Ich habe sogar ein mapMakro eingefügt, um es nützlich und hübsch zu machen.


10
Ich denke Ken Thompson hat uns alle geschlagen : 0 Byte Code.
dmckee

4
Ich möchte keine vollständige Antwort erstellen, aber ich habe GNU C Klassen hinzugefügt , falls sich jemand dafür interessiert.
Richard J. Ross III

3
Ich bin nicht sicher, ob dies in Frage kommt, aber ich habe ein Beispiel für Fortsetzungen in C geschrieben . Ein bisschen mehr als ein Bildschirm voll.
Luser Droog

1
Mein Dank geht an alle, die diese Frage auferweckt haben. Ich habe eine hervorragende Idee für meine Einreichung.
Jonathan Van Matre

Antworten:


27

OOP-Syntax in Haskell

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

Objekte können Eigenschaften haben:

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

... und Methoden:

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

2
Irgendwo sah ich diesen Operator so geschrieben &und definiert (&) = flip ($).
Swish

6
@swish habe ich nicht verwendet, &da es sich um den unären Operator 'address-of' handelt (die Implementierung von Zeigern in Haskell bleibt als Übung für den Leser).
Lortabac

1
@swish Sie können einen Charakter (und einen Gehirnzyklus) speichern, indem Sieflip id
Sean D

24

goto in JavaScript?

Mein erster Gedanke war ein funktionaler Ansatz - der Funktion einen Parameter hinzuzufügen, der angibt, wo die Ausführung beginnen soll, wobei dieser Parameter mit einer switchAnweisung und einer äußeren Schleife verwendet wird, die die Funktion wiederholt auf ihren eigenen Rückgabewert aufruft . Leider würde dies die Verwendung lokaler Variablen ausschließen, da diese mit jedem Sprung ihre Werte verlieren würden.

Ich könnte eine withAnweisung verwenden und alle Variablendeklarationen an den Anfang der Funktion verschieben, aber es musste einen besseren Weg geben. Es kam schließlich zu mir, die Ausnahmebehandlung von JavaScript zu verwenden . Tatsächlich sagte Joel Spolsky: "Ich halte Ausnahmen für nicht besser als" goto's ... " - offensichtlich eine perfekte Passform.

Die Idee war, eine Endlosschleife in eine Funktion zu setzen, die nur durch eine returnAnweisung oder eine nicht erfasste Ausnahme beendet wird. Alle GOTOS, die als Ausnahmen behandelt werden, würden in der Schleife abgefangen, um ihre Beendigung zu verhindern. Hier ist das Ergebnis dieses Ansatzes:

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;
}

Sie können es wie folgt verwenden - auch im strengen ES5-Modus - außer im Internet Explorer ( Demo ):

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

[Aus irgendeinem Grund kann Internet Explorer den Code einer anonymen Funktion nicht auswerten. Daher muss der Funktion ein Name zugewiesen werden (bevor sie neu geschrieben wird), und sie muss unter diesem Namen aufgerufen werden. Natürlich würde das wahrscheinlich gegen die Regeln des strengen Modus verstoßen.]

Dies erlaubt nicht, zu einer Anweisung zu springen, die sich in einem Block befindet (bis Konstrukte wie Duffs Gerät legal sind), aber wir können uns damit befassen (eine andere, selbstausführende, umgeschriebene Funktion), oder?


1
Süss! Gute Arbeit, um es einfach zu halten. Eine interessante Kleinigkeit: Wäre gotoes vollständig in JavaScript implementiert (wo Sie gotoaus jedem Bereich herausspringen könnten, sogar aus einer Funktion ), würde dies die Unterstützung von Fortsetzungen implizieren.
Joey Adams

22

#define in Java

Ich dachte, es würde Spaß machen, Makros in Java zu implementieren.

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("."));
    }

}

Beispielnutzung (wird in zuvor veröffentlichten Code konvertiert; lasst es uns komisch machen):

#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;
    _

_

7
Ich scrollte durch den zweiten Block und mein einziger Gedanke war "... das Kaninchenloch runter."
Soham Chowdhury

18

Foreach in C

Arrays iterieren (funktioniert für statische Arrays, nicht für solche, die über einen Zeiger empfangen werden)

//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++)

Um es zu testen:

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);
}

Ergebnis:

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

17

Eigenschaften in C

Tomasz Wegrzanowski implementierte Eigenschaften in normalem C, indem er das Programm absichtlich in Fehler umwandelte , wenn auf die Eigenschaft zugegriffen wurde.

Ein Objekt mit einer "Eigenschaft" wird eingerichtet, indem ein Objekt erstellt wird, structdas sich über mehrere Seiten erstreckt. Dadurch wird sichergestellt, dass die Speicheradresse der Eigenschaft auf einer anderen Seite als die tatsächlichen Datenelemente liegt. Die Seite der Eigenschaft ist als nicht zugänglich markiert, was garantiert, dass der Versuch, auf die Eigenschaft zuzugreifen, zu einem Segfehler führt. Ein Fehlerbehandler ermittelt dann, welcher Eigenschaftszugriff den Segfault verursacht hat, und ruft die entsprechende Funktion auf, um den Wert der Eigenschaft zu berechnen, der unter der Speicheradresse der Eigenschaft gespeichert wird.

Der Fehlerbehandler markiert die Datenseite auch als schreibgeschützt, um sicherzustellen, dass der berechnete Wert konsistent bleibt. Wenn Sie das nächste Mal versuchen, in ein Datenelement zu schreiben, wird ein Segfault ausgelöst, dessen Handler die Datenseite als schreibgeschützt und die Eigenschaftsseite als nicht zugreifbar festlegt (was darauf hinweist, dass sie neu berechnet werden muss).


15

Berechnet in Common Lisp

Ich habe anfangs come-from implementiert. Aber das war nicht gut genug.

Inspiriert von dem berechneten goto, entschied ich mich für die Implementierung von computed come-from.

(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)))))))))

Anwendungsbeispiele

(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.

Bei jeder Herkunftsdeklaration im Tagbody wird bei jedem Etikett geprüft, ob die Herkunftsvariable mit dem aktuellen Etikett übereinstimmt, und in diesem Fall wird zur entsprechenden Herkunftsdeklaration gesprungen.

Grüße

(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

"Auto-Strings" in Ruby

Der Code ist ganz einfach:

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

Jetzt kannst du tun

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

Fügen Sie PHP Makros hinzu

Wir können nur den C-Präprozessor für diese Aufgabe verwenden.

Ein PHP-Skript:

<?php

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

function test() {
        ERROR(Oops);
}

Pipe es obwohl cpp:

cpp < test.php

Ergebnis:

<?php

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

Bricht das nicht mit PHP-Funktionen, die es in C nicht gibt? Wie Heredocs. Tatsächlich war das C PP ziemlich eng mit Cs Grammatik verbunden.
Joey

1
Ich denke, der Präprozessor lexiert nur die Eingabe, ohne zu versuchen, einen Sinn daraus zu machen. An <<<HEREDOCist nicht mehr als 3 niedriger als oder linksverschoben und ein Bezeichner :-) Dies führt jedoch eine Makrosubstitution in heredoc-Strings durch.
Arnaud Le Blanc

Der C-Präprozessor fügt der Ausgabe zusätzlichen Müll hinzu, sodass Ihr Beispiel nicht wie erwartet funktioniert
anonymer Feigling,

1
Das grep -v ^#könnte man beheben. Ich denke, das ist genug für diese Frage :-)
Arnaud Le Blanc

10

Pattern Matching Guards in 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

Der Funktionsumfang umfasst 288 Zeichen.

Mit Pattern Matching Guards können Sie abhängig von den Argumentwerten völlig unterschiedliche Funktionen verwenden. Obwohl es leicht mit einer Reihe von ifAnweisungen emuliert werden kann, können Pattern-Matching- Guards helfen, Codeabschnitte zu trennen, und es ist eine gute Ausrede, verrückte Metaprogrammierungen vorzunehmen.

pattern_matchist ein Dekorateur, der eine neue Funktion erstellt, die Pattern Matching Guards implementiert . Die Bedingungen für jede "Unterfunktion", die in jedem Dokumentstring in Zeilen angegeben sind, die mit einer Pipe ( |) beginnen. Wenn alle Bedingungen wahrheitsgemäß ausgewertet werden, wird diese Version der Funktion ausgeführt. Die Funktionen werden der Reihe nach getestet, bis eine Übereinstimmung gefunden wird. Andernfalls Nonewird zurückgegeben.

Ein Beispiel soll dazu beitragen, Folgendes zu verdeutlichen:

@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

In Haskell nennt man das Wachen , nicht Mustervergleich. In Haskell können Sie beim Pattern Matching sagen f [a,b,c] = ..., dass das Argument nicht nur mit einem Prädikat verglichen wird, sondern bei erfolgreicher Übereinstimmung auch die entsprechenden Variablen gebunden werden. Das ist aber immer noch ziemlich cool.
Joey Adams

D'oy! Vielen Dank für die Korrektur! Ich habe auch über Haskell nachgedacht und mich speziell darauf konzentriert, eine Funktion mit zwei verschiedenen Prädikaten (dh f (x:xs) = ...und f [] = ...) zu definieren. Irgendwie habe ich Wachen hineingezogen, aber von dort habe ich die genommen |.
zbanks

Dies ist keine Code-Golf-Herausforderung. Sie können ausführlicher (und lesbarer) sein, wenn Sie möchten! :)
ReyCharles


7

Benutzerdefinierte Operatoren in Lua

Pogs haben das Überladen von Operatoren in Lua geschickt missbraucht , um die Definition von benutzerdefinierten Infix-Operatoren zu ermöglichen. Ich habe dies erweitert, um das Aufteilen von Operatoren zu unterstützen (teilweise Anwenden eines Operators mit einem der Operanden) und das Aufrufen des resultierenden Objekts, als ob es eine Funktion wäre.

---- 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

Mehrzeilige Zeichenfolgen in Javascript

In dieser aufwändigen Syntax für mehrzeilige Zeichenfolgen wird jeder mehrzeiligen Zeichenfolge (function(){/*eine neue Zeile vorangestellt, gefolgt von einer neuen Zeile und */}+'').split('\n').slice(1,-1).join('\n').

Mit dieser erstaunlichen, intuitiven Syntax können wir endlich mehrzeilige Zeichenfolgen verwenden:

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!!!

Für Leute, die unsere einfache Syntax nicht mögen, haben wir einen Compiler für unsere fabelhafte neue Sprache:

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

das gleiche Beispiel in der kompilierten Sprachversion:

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

1
Aus irgendeinem Grund kann ich */meine mehrzeiligen Zeichenfolgen nicht einfügen. Das ist super nervig, wenn man Regexps in die Saiten einbindet!
FireFly

@FireFly Eigentlich denke ich, dass das immer noch funktioniert. Die Syntex-Hervorhebung wird jedoch seltsam.
stolzer Haskeller

6

Aufteilbare Liste in C # (wie Python)

Ich mochte immer die Slice-Notation von Python und wünschte, sie wäre in C # verfügbar.

Verwendungszweck:

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)

Code, alles andere als fehlerfrei:

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;
        }
    }
}

Ich habe vor langer Zeit darum gebeten, Slicing in .NET aufzunehmen. Es wird immer noch einfach ignoriert :(
Ray

6

Machen Sie C einfacher

Mit diesem Code können Sie C-Programme schreiben, die eher einer Skriptsprache ähneln. Es enthält Schlüsselwörter wie 'var', 'is', 'string', 'plus', 'equal' und mehrere andere. Es funktioniert mit vielen define-Anweisungen.

// 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

Auf diese Weise können Sie Code wie folgt schreiben:

/*
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

Das Obige wird erweitert zu:

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);
}

Wahrscheinlich nicht übermäßig nützlich, aber ich fand es ziemlich interessant, dass Sie im Wesentlichen eine ganze Programmiersprache durch eine Reihe von #defines erstellen können .


Das sieht aus wie ein Javascript / Ruby-Mashup ...
Beta Decay

Es gibt so gut wie keine Obergrenze dafür - mit komplex genug #defines können Sie Ihrer Sprache sogar Dinge wie Ausnahmebehandlung und Garbage Collection zuweisen, während die grundlegende C-Ebene darunter bleibt.
Leushenko

5

Tcl

Tcl hat keine do ... whileoder do ... untilso ...

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
            }
        }
    }
}

Beispiel:

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

uplevel Führt ein Skript im Bereich des Aufrufers aus.


5

Springen in PostScript

Mein erster Gedanke war, dass ich mich mit dem Exec-Stack herumschlagen müsste, so dass dieser Fehlstart den Fortsetzungsoperator ausgräbt, um von Ghostscript (oder Xpost) gestoppt zu werden.

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

Aber es ist einfacher als das. Da die Dateiposition für alle Duplikate des Datei-Handles gleich ist ( setfilepositionverbraucht sein Argument, daher ist dies die einzige nützliche Semantik für diese Funktion).

/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 =

Es druckt 5.

Es gibt einige Einschränkungen bei den oben genannten. Der Sprung ist nicht unmittelbar, sondern erfolgt, wenn der If-Body zur obersten Ebene zurückkehrt und der Interpreter erneut aus der Datei liest (anstatt aus dem Array zu lesen, das den If-Body enthält). Zu diesem Zeitpunkt wurde die Datei neu positioniert und das 'goto' wird wirksam.


Und es sind nur Definitionen in einem Wörterbuch, sodass Sie für die Beschriftungen fast jeden Typ verwenden können.
Luser Droog

Sie können auch absolute Sprünge ausführen currentfile <pos> setfileposition, indem Sie die Bytes vom Dateianfang an zählen.
Luser Droog

4

Symbol#to_proc mit Argumenten in Ruby

Symbol#to_procist wahrscheinlich einer meiner Lieblingstricks, um wirklich prägnanten Ruby-Code zu schreiben. Angenommen, Sie haben

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

und Sie möchten den Inhalt von numsund textin Floats bzw. Großbuchstaben konvertieren . Symbol#to_procermöglicht es Ihnen, Code wie folgt zu verkürzen:

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

dazu:

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

Genial! Aber was ist, wenn wir jedes Element in numsdie iMacht bringen oder jedes Vorkommen von sdurch *in ersetzen wollen text? Gibt es eine Möglichkeit, Code wie diesen zu verkürzen?

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

Leider gibt es keine einfache Möglichkeit, Argumente bei der Verwendung zu übergeben Symbol#to_proc. Ich habe es auf mehrere Arten gesehen, aber wahrscheinlich beinhalten zwei der klügsten und nützlichsten das Patchen der SymbolKlasse mit Affen [ 1 , 2 ]. Ich werde den ersten Weg unten veranschaulichen.

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

Jetzt können Sie Dinge tun wie:

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

3

JavaScript für jeden

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);

Ausgabe

Seattle, WA
New York, NY
Chicago, IL

Alternative Syntax, eher wie 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);
});

Dies ist kein einfacher Foreach, aber interessanter. Es prüft die Argumentliste der konsumierenden Funktion. Mit diesem Trick könnten Sie noch weiter gehen und wirklich coole Sachen machen.
Joey Adams

1
Ich wollte für jeden einen Tcl-Stil. Ich habe einen etwas anderen Ansatz hinzugefügt, der eher Tcl ähnelt.
Wolfhammer

2

Gotos in Haskell

Die Grundidee ist, dass gotos mit der letzten Anweisung in do-notations teilweise simuliert werden können. zum Beispiel:

main = do
  loop:
  print 3
  goto loop

ist äquivalent zu

main = do
  loop
loop = do
  print 3
  loop

da die Ausführung zur letzten Anweisung springt, ist es optimal, gotos auszudrücken.

gotos springt nur, wenn sie sich dodirekt im Block einer Toplevel-Definition befinden. Es heißt eigentlich "rufe x auf und ignoriere den Rest der lexikalisch gesehenen Anweisungen" und nicht "alle x und ignoriere den Rest der Anweisungen", wie ein echtes goto.

Das größte Problem ist, dass, wenn es keine Möglichkeit gibt, die Ausführung mitten in einer E / A-Aktion zu beenden, returndies auch nicht der Fall ist. returntut nichts, wenn es nicht die letzte Aussage ist.

Dies überwindet dies, indem der Rest der Anweisungen von einem anderen doBlock erfasst wird .

goto loop
print 3

wird

const loop $ do
print 3

Die print 3Anweisung wird vom doBlock erfasst und wird so loopzur letzten Anweisung.

Diese Umwandlung unterstützt auch Variablen, die im Bereich der Aktionen vorhanden sind. Dazu werden die Variablen, die sich im Gültigkeitsbereich befinden, gespeichert und an die Aktionen übergeben. zum Beispiel:

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

das übersetzt einfach in:

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

einige Notizen:

Außerdem wird eine return undefinedAnweisung hinzugefügt, um sicherzustellen, dass der Erfassungsblock donicht leer ist.

weil der Erfassungsblock manchmal mehrdeutig ist do, anstatt dass constwir ihn verwenden asTypeOf. Dies ist dasselbe wie const, erfordert jedoch, dass beide Parameter den gleichen Typ haben.

die eigentliche Implementierung (in 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
            }
        })
}

eine exampe:

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

wird:

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

Ausgabe:

a
c

Es sollte klargestellt werden, dass es sich returnbei Haskell um eine reguläre Funktion handelt, die nichts mit dem Schlüsselwort in C / etc zu tun hat.
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)

Verwendungszweck

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

Beispiel Testfall

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

print "Asdf"

Beispiel für eine Testfallausgabe

Asdf

Nur ein bisschen Spaß mit exec (). Kann einen maximalen Rekursionstiefenfehler auslösen, wenn er nicht richtig verwendet wird.


-2

// JavaScript importieren, ohne das Skript-Tag in einer HTML-Seite zu verwenden

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

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

Es ist lahm, ja, ich weiß. Länge: 99


@ user2509848: Dieser Thread ist nicht mit Code Golf markiert.
Joey Adams

Was Sie gepostet haben, muss mit einem scriptTag versehen werden. Wo genau ist dann die neue Funktion?
Manatwork

@ JoeyAdams Ups, sorry.
Hosch250
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.