เพิ่มคุณสมบัติให้กับภาษาการเขียนโปรแกรม [ปิด]


55

งานของคุณคือการแก้ไขคุณลักษณะของภาษาโปรแกรมโดยการนำไลบรารี่ที่ฉลาดมากหรือโดยการประมวลผลข้อความอินพุตและ / หรือปรับแต่งกระบวนการรวบรวม

ไอเดีย:

  • เพิ่มงานนำเสนอสไตล์ PHP interleaving ไปที่ C (เช่น<?c printf("Hello,"); ?> world!)
  • เพิ่มโอเปอเรเตอร์การรวมตัวกันเป็นโมฆะในภาษาเหล่านั้นที่ไม่ใช่ C #
  • เพิ่มมาโครใน PHP
  • เพิ่มgotoไปยัง JavaScript
  • เพิ่มการจับคู่รูปแบบเป็นภาษา X
  • เพิ่มการสนับสนุน namespace ให้กับภาษาที่ไม่มี
  • ทำให้ C ดูเหมือน PHP
  • ทำให้ Haskell ดูเหมือน Pascal
  • ... (อย่าลังเลที่จะโพสต์แนวคิดในส่วนความคิดเห็น)

กฎ:

  • นำสิ่งที่จะตาราง อย่าเพียงแค่พูดว่า "เทมเพลตแฮสเค็ลล์" เพื่อเพิ่มเครื่องมืออำนวยความสะดวกในการ metaprogramming ให้กับแฮสเคลล์ นี่ไม่ใช่ StackOverflow
  • การใช้งานทั้งหมดควรจะพอดีในหนึ่งหน้าจอ (ไม่นับตัวอย่าง)
  • อย่าโฮสต์รหัสบนไซต์ภายนอกโดยเฉพาะสำหรับงานนี้
  • คุณสมบัติที่น่าประทับใจหรือน่าประหลาดใจที่สุดชนะ

ไม่ต้องกังวลกับการนำคุณลักษณะไปใช้อย่างถูกต้อง 100% ไกลจากมัน! ความท้าทายหลักคือการค้นหาว่าคุณต้องการทำอะไรและตัดรายละเอียดออกไปอย่างชั่วร้ายจนกว่าภารกิจที่คุณวางแผนไว้จะเป็นไปได้

ตัวอย่าง:

เพิ่มตัวดำเนินการแลมบ์ดาในภาษาการเขียนโปรแกรม C

วิธีการเริ่มต้น:

โอเคฉันรู้ว่าฉันต้องการใช้libgcดังนั้นลูกแกะของฉันจะแก้ปัญหา funarg ที่ขึ้นและลง ฉันเดาสิ่งแรกที่ฉันต้องทำคือเขียน / หา parser สำหรับภาษาการเขียนโปรแกรม C จากนั้นฉันจะต้องเรียนรู้ทั้งหมดเกี่ยวกับระบบชนิดของ C ฉันต้องหาวิธีที่จะเข้าใจมันให้ดีที่สุดเท่าที่จะเป็นไปได้ ฉันจะต้องใช้การอนุมานประเภทหรือฉันควรเพียงต้องการให้พิมพ์พารามิเตอร์ที่เป็นทางการตามที่กำหนด? แล้วฟีเจอร์ที่บ้าคลั่งเหล่านี้ใน CI ยังไม่รู้อะไรบ้าง?

เป็นที่ชัดเจนว่าการใช้แลมบ์ดาใน C อย่างถูกต้องจะเป็นเรื่องใหญ่ ลืมความถูกต้องไปเสีย! ลดความซับซ้อนลดความซับซ้อน

ที่ดีกว่า:

สกรู funargs ขึ้นใครต้องการ 'em ฉันอาจจะสามารถที่จะทำบางสิ่งบางอย่างที่หากินกับ GNU ซีฟังก์ชั่นที่ซ้อนกันและการแสดงออกทางคำสั่ง ฉันต้องการแสดงการแปลงทางวากยสัมพันธ์ที่น่าทึ่งใน C ด้วยรหัสตัวย่อ, แฮ็ค แต่ฉันไม่จำเป็นต้องใช้โปรแกรมแยกวิเคราะห์สำหรับเรื่องนี้ ที่สามารถรออีกวัน

ผลลัพธ์ (ต้องมี 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;
}

นั่นเป็นเรื่องง่ายใช่มั้ย ฉันยังขว้างmapมาโครเพื่อให้มีประโยชน์และสวย


10
ฉันคิดว่าKen Thompson ทำให้เราทุกคนชนะ : 0 ไบต์ของรหัส
dmckee

4
ฉันไม่ต้องการสร้างคำตอบแบบเต็ม แต่ฉันได้เพิ่มคลาสใน GNU Cในกรณีที่ใครสนใจ
Richard J. Ross III

3
ไม่แน่ใจว่านี้มีคุณสมบัติ แต่ผมเคยเขียนตัวอย่างของตใน C แม้ว่าจะมากกว่าหน้าจอเพียงเล็กน้อย
luser droog

1
ฉันขอขอบคุณผู้ที่ฟื้นคืนชีพของคำถามนี้; ฉันมีความคิดที่ยอดเยี่ยมสำหรับการส่งของฉัน
Jonathan Van Matre

2
เพิ่มแลมบ์ดาใน C ... เฮ้อย่ามองฉันแบบนั้น
Leushenko

คำตอบ:


27

ไวยากรณ์ OOP ใน Haskell

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

วัตถุสามารถมีคุณสมบัติ:

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

... และวิธีการ:

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

2
ที่ไหนสักแห่งที่ฉันเห็นผู้ประกอบการนี้เขียนเป็นและที่กำหนดไว้เช่นนี้& (&) = flip ($)
หวด

6
@ ต้องการฉันไม่ได้ใช้&เพราะเป็น 'ที่อยู่ของ' ผู้ประกอบการเอก (การดำเนินการของตัวชี้ใน Haskell ถูกทิ้งไว้เป็นแบบฝึกหัดสำหรับผู้อ่าน)
lortabac

1
@ หวังว่าคุณสามารถบันทึกตัวละคร (และวงจรสมอง) โดยใช้flip id
Sean D

24

goto ใน JavaScript?

ความคิดแรกของฉันเป็นวิธีการทำงาน - การเพิ่มพารามิเตอร์เพื่อฟังก์ชั่นเพื่อระบุตำแหน่งที่ควรจะเริ่มต้นดำเนินการโดยใช้ที่มีswitchคำสั่งและนอกวงซ้ำ ๆ เรียกฟังก์ชั่นในค่าตอบแทนของตัวเอง น่าเสียดายที่นั่นจะขัดขวางการใช้ตัวแปรท้องถิ่นเนื่องจากพวกเขาจะสูญเสียคุณค่าของพวกเขาไปกับการข้ามไป

ฉันสามารถใช้withคำสั่งและย้ายการประกาศตัวแปรทั้งหมดไปยังจุดเริ่มต้นของฟังก์ชัน แต่ต้องมีวิธีที่ดีกว่า ในที่สุดมันก็มาหาผมที่จะใช้จัดการข้อยกเว้นของ JavaScript ในความเป็นจริง Joel Spolsky กล่าวว่า"ฉันคิดว่าข้อยกเว้นจะไม่ดีไปกว่า" goto's ... " - เห็นได้ชัดว่าเป็นแบบที่สมบูรณ์แบบ

ความคิดคือการใส่วงวนไม่สิ้นสุดภายในฟังก์ชันสิ้นสุดลงโดยreturnคำสั่งหรือข้อยกเว้นที่ไม่ได้ตรวจสอบ gotos ทั้งหมดซึ่งถือว่าเป็นข้อยกเว้นจะถูกตรวจจับภายในลูปเพื่อป้องกันการยกเลิก นี่คือผลลัพธ์ของวิธีการนั้น:

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

คุณสามารถใช้สิ่งนี้ - แม้ในโหมดเข้มงวด ES5 - ยกเว้นใน Internet Explorer ( สาธิต ):

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

[Internet Explorer ด้วยเหตุผลบางอย่างล้มเหลวในการประเมินรหัสของฟังก์ชั่นที่ไม่ระบุชื่อดังนั้นหนึ่งจะต้องให้ชื่อฟังก์ชั่น (ก่อนที่จะเขียนใหม่) และเรียกมันโดยใช้ชื่อนั้น แน่นอนว่าอาจเป็นการฝ่าฝืนกฎของโหมดที่เข้มงวด]

สิ่งนี้ไม่อนุญาตให้ข้ามไปยังข้อความที่อยู่ในบล็อก (จนกว่าจะสร้างเป็นอุปกรณ์ของ Duff ให้ถูกต้องตามกฎหมาย) แต่เราสามารถจัดการกับสิ่งนั้นได้ (ฟังก์ชั่นเขียนใหม่ที่ทำงานด้วยตัวเอง) ใช่ไหม?


1
หวาน! งานที่ดีทำให้มันง่าย เรื่องไม่สำคัญที่น่าสนใจ: หากgotoมีการใช้งานอย่างสมบูรณ์ใน JavaScript (ไปยังที่ที่คุณสามารถใช้gotoเพื่อกระโดดจากขอบเขตใด ๆ แม้แต่ฟังก์ชั่น ) มันจะบ่งบอกถึงการสนับสนุนต่อเนื่อง
Joey Adams

22

#define ใน Java

ฉันคิดว่าการใช้มาโครใน 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("."));
    }

}

ตัวอย่างการใช้งาน (แปลงเป็นรหัสที่โพสต์ก่อนหน้านี้ทำให้มันแปลก):

#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
ฉันเลื่อนผ่านบล็อกที่สองและความคิดเดียวของฉันคือ "... ลงไปในโพรงกระต่าย"
Soham Chowdhury

18

Foreach ใน C

ทำซ้ำอาร์เรย์ (ใช้ได้กับอาร์เรย์แบบคงที่ไม่ใช่แบบที่ได้รับผ่านตัวชี้)

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

เพื่อทดสอบ:

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

ผลลัพธ์:

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

17

คุณสมบัติใน C

Tomasz Wegrzanowski ดำเนินการ คุณสมบัติในที่ราบ C โดยตั้งใจแบ่งโปรแกรมเมื่อมีการเข้าถึงคุณสมบัติ

มีการตั้งค่าวัตถุที่มี "คุณสมบัติ" โดยการสร้างstructที่ข้ามหลายหน้าเพื่อให้แน่ใจว่าที่อยู่หน่วยความจำของคุณสมบัติอยู่ในหน้าอื่นจากสมาชิกข้อมูลจริง หน้าของคุณสมบัติถูกทำเครื่องหมายว่าไม่สามารถเข้าถึงได้รับประกันว่าการพยายามเข้าถึงคุณสมบัติจะทำให้เกิด segfault ตัวจัดการข้อผิดพลาดจะระบุว่าการเข้าถึงคุณสมบัติใดทำให้เกิด segfault และเรียกใช้ฟังก์ชันที่เหมาะสมเพื่อคำนวณค่าของคุณสมบัติซึ่งได้รับการจัดเก็บที่ที่อยู่หน่วยความจำของคุณสมบัติ

ตัวจัดการข้อผิดพลาดยังทำเครื่องหมายหน้าข้อมูลเป็นแบบอ่านอย่างเดียวเพื่อให้แน่ใจว่าค่าที่คำนวณยังคงสอดคล้องกัน เมื่อคุณพยายามเขียนข้อมูลสมาชิกครั้งถัดไปสิ่งนั้นจะเรียก segfault ซึ่งตัวจัดการตั้งค่าหน้าข้อมูลเป็นอ่าน - เขียนและหน้าคุณสมบัติเป็นการไม่เข้าถึง (ระบุว่าจำเป็นต้องทำการคำนวณใหม่)


15

คำนวณมาจาก Common LISP

ฉันเริ่มใช้มาจาก แต่นั่นไม่ดีพอ

แรงบันดาลใจจากการคำนวณของ goto ฉันตัดสินใจที่จะนำมาคำนวณ

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

ตัวอย่างการใช้งาน

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

สำหรับการประกาศที่มาจากแต่ละครั้งใน tagbody จะตรวจสอบที่แต่ละป้ายถ้าตัวแปรที่มาจากเท่ากับป้ายปัจจุบันและถ้าเป็นเช่นนั้นข้ามไปยังการประกาศมาที่สอดคล้องกัน

รู้ตัว

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


13

เพิ่มมาโครใน PHP

เราสามารถใช้ตัวประมวลผลล่วงหน้า C สำหรับงานนี้ได้

สคริปต์ PHP:

<?php

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

function test() {
        ERROR(Oops);
}

ท่อแม้ว่า cpp:

cpp < test.php

ผลลัพธ์:

<?php

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

มันจะไม่แตกกับฟีเจอร์ PHP ที่ไม่มีอยู่ใน C ใช่ไหม เช่น heredocs Afair the C PP นั้นสัมพันธ์กับไวยากรณ์ของ C ค่อนข้างแน่น
Joey

1
ฉันคิดว่าตัวประมวลผลล่วงหน้าป้อนเฉพาะอินพุตโดยไม่พยายามทำให้เข้าใจ An <<<HEREDOCคืออะไรที่ต่ำกว่าหรือกะซ้ายมากกว่า 3 ตัวและตัวระบุ :-) ซึ่งจะทำการทดแทนแมโครในสตริง heredoc
Arnaud Le Blanc

ตัวประมวลผลล่วงหน้า C เพิ่มขยะพิเศษให้กับเอาต์พุตดังนั้นตัวอย่างของคุณจะไม่ทำงานตามที่คาดไว้
คนขี้ขลาดนิรนาม

1
สิ่งที่ต้องgrep -v ^#แก้ไขนั่น ฉันเดาว่านี่ก็เพียงพอแล้วสำหรับคำถามนี้ :-)
Arnaud Le Blanc

10

แพทเทิร์นการจับคู่รูปแบบใน 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

เนื้อหาของฟังก์ชันมีความยาว 288 ตัวอักษร

การจับคู่รูปแบบช่วยให้คุณสามารถใช้ฟังก์ชั่นที่แตกต่างกันอย่างสมบูรณ์ขึ้นอยู่กับค่าของอาร์กิวเมนต์ แม้ว่าจะสามารถเลียนแบบชุดifข้อความได้อย่างง่ายดายแต่การ์ดจับคู่รูปแบบสามารถช่วยแยกส่วนของรหัสได้และเป็นข้อแก้ตัวที่ดีในการทำ metaprogramming

pattern_matchเป็นมัณฑนากรที่สร้างฟังก์ชั่นใหม่ที่ใช้ยามจับคู่รูปแบบ เงื่อนไขสำหรับ "ฟังก์ชั่นย่อย" แต่ละอันที่กำหนดในแต่ละ docstring บนบรรทัดที่ขึ้นต้นด้วยไพพ์ ( |) หากเงื่อนไขทั้งหมดประเมินตามความเป็นจริงจะมีการเรียกใช้ฟังก์ชันเวอร์ชันนั้น ฟังก์ชั่นจะถูกทดสอบตามลำดับจนกว่าจะพบการแข่งขัน มิฉะนั้นNoneจะถูกส่งกลับ

ตัวอย่างจะช่วยชี้แจง:

@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

ใน Haskell สิ่งนี้เรียกว่าguardsไม่ใช่รูปแบบที่ตรงกัน ใน Haskell การจับคู่รูปแบบช่วยให้คุณพูดf [a,b,c] = ...ได้ซึ่งไม่เพียงทดสอบอาร์กิวเมนต์กับเพรดิเคตเท่านั้น แต่มันจะผูกตัวแปรตามลำดับเมื่อการจับคู่สำเร็จ มันก็ยังค่อนข้างเท่ห์
Joey Adams

D'เอ๋ย! ขอบคุณสำหรับการแก้ไข! ฉันกำลังคิดเกี่ยวกับ Haskell ด้วยเช่นกันโดยเน้นไปที่การกำหนดฟังก์ชั่นด้วยภาคแสดงสองแบบ (เช่นf (x:xs) = ...และf [] = ...) ยังไงก็เถอะฉันเข้าไปที่นั่น แต่ที่ฉันเอา|มา
zbanks

นี่ไม่ใช่ความท้าทายในการเล่นกอล์ฟ คุณสามารถ verbose เพิ่มเติม (และอ่านได้) ถ้าคุณต้องการ! :)
ReyCharles


7

ผู้ประกอบการที่กำหนดเองใน Lua

Pogs ใช้งานโอเปอเรเตอร์ที่ใช้งานหนักจนเกินไปใน Luaเพื่อให้ผู้ให้บริการระบบมัดแบบกำหนดเองสามารถกำหนดได้ ฉันได้ขยายสิ่งนี้เพื่อสนับสนุนการแบ่งส่วนโอเปอเรเตอร์ (บางส่วนใช้โอเปอเรเตอร์ที่มีตัวถูกดำเนินการทั้งสอง) และเรียกวัตถุผลลัพธ์ราวกับว่ามันเป็นฟังก์ชัน

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

สตริงหลายเส้นใน javascript

ในไวยากรณ์ที่ซับซ้อนนี้สำหรับสตริง multiline ทุกสตริง multiline จะนำหน้าด้วยและขึ้นบรรทัดใหม่และตามมาด้วยการขึ้นบรรทัดใหม่และ(function(){/**/}+'').split('\n').slice(1,-1).join('\n')

ด้วยการใช้ไวยากรณ์ที่น่าอัศจรรย์และใช้งานง่ายนี้ในที่สุดเราก็สามารถใช้สตริงหลายบรรทัด:

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

สำหรับผู้ที่ไม่ชอบไวยากรณ์ที่เรียบง่ายของเราเรามีผู้แปลภาษาใหม่ที่ยอดเยี่ยมของเรา:

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

ตัวอย่างเดียวกันในเวอร์ชันภาษาที่คอมไพล์:

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

1
ด้วยเหตุผลบางอย่างฉันไม่สามารถใส่*/สายหลายบรรทัดได้ นี่เป็นเรื่องที่น่ารำคาญมากเมื่อรวม regexps ในสตริงด้วย!
FireFly

@FireFly จริงๆแล้วฉันคิดว่ามันยังใช้งานได้ การเน้นสี syntex นั้นแปลก
ภูมิใจ haskeller

6

รายชื่อ Sliceable ใน C # (เช่น Python)

ฉันชอบสัญกรณ์แบบไพ ธ อนเสมอและหวังว่าจะมีให้ใน C #

การใช้งาน:

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)

รหัสห่างไกลจากข้อผิดพลาดการพิสูจน์:

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

ฉันขอแบ่งเป็นเวลานานแล้วที่จะรวมอยู่ใน. NET มันก็ยังไม่สนใจ :(
Ray

6

ทำให้ C ง่ายขึ้น

รหัสนี้ช่วยให้คุณสามารถเขียนโปรแกรม C ที่มีลักษณะคล้ายกับภาษาสคริปต์ได้อีกเล็กน้อย มันมีคำหลักเช่น 'var', 'is', 'string', 'plus', 'เท่ากับ' และอีกหลายคำ มันทำงานผ่านคำสั่งกำหนดจำนวนมาก

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

สิ่งนี้อนุญาตให้คุณเขียนโค้ดเช่น:

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

ข้างต้นได้รับการขยายไปที่:

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

อาจจะไม่ได้มีประโยชน์มากเกินไป แต่ผมพบว่ามันค่อนข้างน่าสนใจที่คุณเป็นหลักสามารถสร้างโปรแกรมภาษาทั้งผ่านพวงของ#defines


ดูเหมือนว่า mashup ของ Javascript / Ruby ...
Beta Decay

ไม่มีข้อ จำกัด ด้านบน - มีความซับซ้อนเพียงพอ#defineคุณสามารถให้ภาษาของคุณเช่นการจัดการข้อยกเว้นและการเก็บขยะในขณะที่ยังคงรักษาเลเยอร์ C พื้นฐานไว้ข้างใต้
Leushenko

5

Tcl

Tcl ไม่มีdo ... whileหรือdo ... untilดังนั้น ...

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

ตัวอย่าง:

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

uplevel รันสคริปต์ในขอบเขตผู้โทร


5

ไปที่ PostScript

ความคิดแรกของฉันคือการที่ฉันต้องยุ่งเกี่ยวกับสแต็ค exec ดังนั้นการเริ่มต้นที่ผิดนี้จะขุดโอเปอเรเตอร์ต่อเนื่องเพื่อหยุดจาก ghostscript (หรือ xpost)

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

แต่มันง่ายกว่านั้น เนื่องจากตำแหน่งไฟล์เหมือนกันสำหรับการทำซ้ำทั้งหมดของการจัดการไฟล์ ( setfilepositionใช้อาร์กิวเมนต์ของมันดังนั้นนี่เป็นความหมายที่มีประโยชน์สำหรับฟังก์ชันนั้นเท่านั้น)

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

5มันพิมพ์

มีข้อ จำกัด บางประการด้านบน การกระโดดไม่ได้เกิดขึ้นทันที แต่เกิดขึ้นเมื่อ if-body กลับสู่ระดับบนสุดและล่ามจะอ่านจากไฟล์อีกครั้ง (แทนที่จะอ่านจากอาร์เรย์ที่มี if-body) ณ จุดนั้นไฟล์ถูกเปลี่ยนตำแหน่งและ 'goto' จะมีผล


และมันเป็นเพียงคำจำกัดความในพจนานุกรมดังนั้นคุณจึงสามารถใช้ป้ายกำกับได้เกือบทุกประเภท
luser droog

นอกจากนี้คุณยังสามารถกระโดดได้แน่นอนด้วยcurrentfile <pos> setfilepositionนับไบต์จากจุดเริ่มต้นของไฟล์
luser droog

4

Symbol#to_proc กับข้อโต้แย้งในทับทิม

Symbol#to_procอาจเป็นหนึ่งในเทคนิคที่ฉันโปรดปรานสำหรับการเขียนรหัส Ruby ที่รวบรัดจริงๆ สมมติว่าคุณมี

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

และคุณต้องการแปลงเนื้อหาของnumsและtextเป็น Floats และตัวพิมพ์ใหญ่คำตามลำดับ Symbol#to_procอนุญาตให้คุณย่อโค้ดเช่นนี้ได้:

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

สำหรับสิ่งนี้:

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

น่ากลัว! แต่ถ้าเราต้องการที่จะเพิ่มในทุกองค์ประกอบnumsไปที่iพลังงาน, th หรือเปลี่ยนทุกการเกิดขึ้นของsที่มี*ในtext? มีวิธีการย่อรหัสเช่นนี้หรือไม่?

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

Symbol#to_procอนิจจาไม่มีวิธีที่ง่ายที่จะผ่านข้อโต้แย้งเมื่อใช้ ฉันเคยเห็นมันทำได้หลายวิธี แต่อาจเป็นวิธีที่ฉลาดและใช้งานได้ดีที่สุดในการSymbolเรียนลิง[ 1 , 2 ] ฉันจะอธิบายวิธีแรกด้านล่าง

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

ตอนนี้คุณสามารถทำสิ่งต่าง ๆ เช่น:

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

เอาท์พุต

Seattle, WA
New York, NY
Chicago, IL

ไวยากรณ์สำรองเช่น 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);
});

นี่ไม่ใช่ foreach ธรรมดา ๆ แต่น่าสนใจกว่า มันตรวจสอบรายการอาร์กิวเมนต์ของฟังก์ชั่นการบริโภค คุณสามารถไปต่อด้วยเคล็ดลับนี้และทำสิ่งที่ยอดเยี่ยมจริงๆ
Joey Adams

1
ฉันกำลังจะไปหาสไตล์ Tcl ฉันได้เพิ่มวิธีการที่แตกต่างออกไปเล็กน้อยซึ่งเหมือนกับ Tcl
wolfhammer

2

Gotos ใน Haskell

แนวคิดพื้นฐานคือ gotos สามารถจำลองได้บางส่วนโดยใช้คำสั่งสุดท้ายในคำdoอธิบายประกอบ ตัวอย่างเช่น:

main = do
  loop:
  print 3
  goto loop

เทียบเท่ากับ

main = do
  loop
loop = do
  print 3
  loop

เนื่องจากการดำเนินการจะข้ามไปยังคำสั่งสุดท้ายจึงเป็นการดีที่สุดที่จะแสดง gotos

เพราะวิธีที่ทำเสร็จแล้ว gotos จะกระโดดก็ต่อเมื่ออยู่ที่doบล็อกของคำจำกัดความระดับสูงโดยตรง มันคือ "call x และเพิกเฉยต่อประโยคที่เหลือที่เห็น " มากกว่า "all x และเพิกเฉยต่อประโยคที่เหลือ" เช่น goto จริง

ปัญหาที่ใหญ่ที่สุดคือเมื่อไม่มีทางที่จะปล่อยให้การดำเนินการจากตรงกลางของการกระทำ IO - แม้returnไม่; returnไม่ทำอะไรเลยเมื่อมันไม่ใช่คำสั่งสุดท้าย

สิ่งนี้จะเอาชนะสิ่งนี้ได้โดยการจับส่วนที่เหลือของข้อความโดยdoบล็อกอื่น

goto loop
print 3

กลายเป็น

const loop $ do
print 3

print 3คำสั่งถูกจับโดยdoบล็อกจึงloopกลายเป็นคำสั่งที่ผ่านมา

การแปลงนี้ยังสนับสนุนตัวแปรที่มีอยู่ในขอบเขตของการกระทำ สิ่งนี้ทำได้โดยการจดจำตัวแปรที่อยู่ในขอบเขตและส่งต่อไปยังการกระทำ ตัวอย่างเช่น:

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

นี่แปลว่า:

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

หมายเหตุบางส่วน:

นอกจากนี้ยังมีการreturn undefinedเพิ่มคำสั่งเพื่อให้แน่ใจว่าdoบล็อกการจับภาพไม่ว่างเปล่า

เพราะบางครั้งมีความคลุมเครือประเภทในdoบล็อกการจับภาพแทนการที่constเราใช้asTypeOfซึ่งเหมือนกันconstแต่ต้องการให้พารามิเตอร์ทั้งสองมีประเภทเดียวกัน

การใช้งานจริง (ใน 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
            }
        })
}

ตัวอย่าง:

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

กลายเป็น:

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

เอาท์พุท:

a
c

เป็นมูลค่าชี้แจงว่าreturnใน Haskell เป็นฟังก์ชั่นปกติและไม่เกี่ยวข้องกับคำหลักใน C / ฯลฯ
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)

การใช้

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

ตัวอย่างทดสอบ

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

print "Asdf"

ตัวอย่างผลลัพธ์ของกรณีทดสอบ

Asdf

สนุกไปกับ exec () นิดหน่อย สามารถเพิ่มข้อผิดพลาดการเรียกซ้ำลึกสูงสุดหากไม่ได้ใช้อย่างถูกต้อง


-2

// นำเข้า javascript โดยไม่เฉพาะเจาะจงโดยใช้แท็กสคริปต์ในหน้า HTML

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

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

มันเป็นง่อยใช่ฉันรู้ ความยาว: 99


@ user2509848: เธรดนี้ไม่ได้ติดแท็กรหัสกอล์ฟ
Joey Adams

สิ่งที่คุณโพสต์ต้องมีscriptแท็กรอบ ๆ ถ้าอย่างนั้นคุณสมบัติใหม่คืออะไร?
จัดการ

@JoeyAdams โอ๊ะโอขออภัย
Hosch250
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.