การจัดรูปแบบไวยากรณ์เหมือนเสียงกระเพื่อม


23

พื้นหลัง

(ขึ้นอยู่กับเรื่องจริงที่ทำให้หัวใจวาย)

ในเวลาของฉันฉันได้เล่นกับ Lisp และภาษาที่คล้ายกันบ่อยครั้ง ฉันเขียนกับพวกเขาแล้ววิ่งตีความตีความออกแบบและสร้างเครื่องเขียนกับพวกเขาสำหรับฉัน ... และถ้ามีสิ่งหนึ่งที่รบกวนจิตใจฉันก็เห็น Lisp ที่ไม่สอดคล้องกับสไตล์การจัดรูปแบบเฉพาะของฉัน

น่าเสียดายที่มีเครื่องมือแก้ไขข้อความ ( ไอ XCode ไอ ) มีแนวโน้มที่จะดึงแท็บและช่องว่างที่สวยงามของฉันเมื่อใดก็ตามที่มีการคัดลอกและวางรหัส ... ใช้ไวยากรณ์ที่คล้ายกับเสียงกระเพื่อมนี้

(A
    (B
        (C)
        (D))
    (E))

( ABCDEฟังก์ชั่นโดยพลการอยู่ที่ไหน)

บรรณาธิการข้อความบางคนฆ่ารหัสที่น่ารักนี้ไปยังจุดสิ้นสุดต่อไปนี้:

(A
(B
(C)
(D))
(E))

ช่างเป็นระเบียบ! ไม่สามารถอ่านได้!

ช่วยฉันออก

ความท้าทาย

เป้าหมายของคุณในความท้าทายนี้คือการใช้ชุดฟังก์ชั่นคั่นด้วยบรรทัดใหม่ในรูปแบบที่อธิบายด้านล่างและกลับมาจัดเรียงที่สวยงามยิ่งขึ้นซึ่งเน้นการอ่านและความสง่างาม

การป้อนข้อมูล

เรากำหนดฟังก์ชันFของNอาร์กิวเมนต์arity เป็นโครงสร้างคล้ายกับต่อไปนี้:

(F (G1 ...) (G2 ...) (G3 ...) ... (GN ...))

G1, G2, ..., GNฟังก์ชั่นทั้งหมดอยู่ที่ไหนและด้วยตนเอง 0ฟังก์ชันarity Aนั้นเรียบง่าย(A)ในขณะที่2ฟังก์ชันarity Bเป็นแบบฟอร์ม(B (...) (...))

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

คุณอาจจะ:

  • วงเล็บมีความสมดุล
  • ฟังก์ชั่นจะไม่ต้องถูกเยื้องมากกว่า 250 ครั้ง
  • ฟังก์ชัน EVERY ถูกล้อมรอบด้วยวงเล็บ: ()
  • ชื่อของฟังก์ชันจะมีอักขระ ASCII ที่พิมพ์ได้เท่านั้น
  • ชื่อของฟังก์ชั่นจะไม่มีวงเล็บหรือช่องว่าง
  • มีบรรทัดใหม่ต่อท้ายที่เป็นตัวเลือกในอินพุต

ผลลัพธ์

รหัสของคุณควรส่งออกชุดฟังก์ชั่นเดียวกันโดยที่การเปลี่ยนแปลงเพียงอย่างเดียวคือการเพิ่มช่องว่างหรือแท็บก่อนหน้าเครื่องหมายวงเล็บหน้าของฟังก์ชั่น ผลลัพธ์ควรเป็นไปตามกฎต่อไปนี้:

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

กฎระเบียบ

ตัวอย่าง

การป้อนข้อมูล:

(A
(B
(C)
(D))
(E))

เอาท์พุท:

(A
    (B
        (C)
        (D))
    (E))

การป้อนข้อมูล:

(!@#$%^&*
(asdfghjklm
(this_string_is_particularly_long
(...))
(123456789)))
(THIS_IS_TOP_LEVEL_AGAIN
(HERE'S_AN_ARGUMENT))

เอาท์พุท:

(!@#$%^&*
    (asdfghjklm
        (this_string_is_particularly_long
            (...))
        (123456789)))
(THIS_IS_TOP_LEVEL_AGAIN
    (HERE'S_AN_ARGUMENT))

การป้อนข้อมูล:

(-:0
(*:0
(%:0
(Arg:6)
(Write:0
(Read:0
(Arg:30))
(Write:0
(Const:-6)
(Arg:10))))
(%:0
(Const:9)
(/:0
(Const:-13)
(%:0
(Arg:14)
(Arg:0)))))
(WriteArg:22
(-:0
(Const:45)
(?:0
(Arg:3)
(Arg:22)
(Arg:0)))))

เอาท์พุท:

(-:0
    (*:0
        (%:0
            (Arg:6)
            (Write:0
                (Read:0
                    (Arg:30))
                (Write:0
                    (Const:-6)
                    (Arg:10))))
        (%:0
            (Const:9)
            (/:0
                (Const:-13)
                (%:0
                    (Arg:14)
                    (Arg:0)))))
    (WriteArg:22
        (-:0
            (Const:45)
            (?:0
                (Arg:3)
                (Arg:22)
                (Arg:0)))))

ยินดีด้วยกับการทำรายการคำถามในเครือข่ายที่น่าสนใจ! : D
Alex A.

@AlexA ไชโย! ความฝันของฉันเป็นจริง : D
BrainSteel

ถ้าไม่มีชื่อฟังก์ชั่นจะเป็นเช่นไร()?
coredump

การเยื้องต้องเป็น> = 3 ช่องว่างหรือเป็นแท็บที่ยอมรับได้?
isaacg

@isaacg คุณสามารถสันนิษฐานได้ว่าฟังก์ชั่นทั้งหมดมีชื่ออยู่ในกรณีนี้ และสิ่งที่ระบบปฏิบัติการ / ภาษาของคุณกำหนดให้เป็นแท็บแนวนอนก็ใช้ได้ ถ้าคุณใช้ช่องว่างจะต้องมีอย่างน้อย 3 ฉันจะชี้แจงว่าเมื่อฉันไปถึงคอมพิวเตอร์ได้ ขอบคุณ!
BrainSteel

คำตอบ:


9

Pyth, 24 20 19 18 ไบต์

FN.z+*ZC9N~Z-1/N\)

เพิ่มตัวนับสำหรับทุกบรรทัดนับจำนวนวงเล็บปิดทั้งหมดที่พบและลบออกจากตัวนับ จากนั้นเราก็เยื้องโดยcounterแท็บ


@Downvoter สนใจที่จะอธิบาย?
orlp

ฉันไม่ได้ลงคะแนน แต่*4เป็นการตั้งค่าที่ยากและซ้ำซ้อน FN.z+*ZC9N~Z-1/N\)ช่วยให้คุณใช้ความกว้างของย่อหน้าของตัวแก้ไขและบันทึกหนึ่งไบต์
Cees Timmerman

ฉันเห็นด้วยแท็บจะสั้นกว่าหนึ่งตัวอักษร หรือ\<tab> C9
isaacg

9

Common Lisp - 486 414 bytes (รุ่น Rube Goldberg)

(labels((p(x d)(or(when(listp x)(#2=princ #\()(p(car x)d)(incf d)(dolist(a(cdr x))(format t"~%~v{   ~}"d'(t))(p a d))(#2# #\)))(#2# x))))(let((i(make-string-input-stream(with-output-to-string(o)(#1=ignore-errors(do(b c)(())(if(member(setq c(read-char))'(#\( #\) #\  #\tab #\newline):test'char=)(progn(when b(prin1(coerce(reverse b)'string)o))(#2# c o)(setq b()))(push c b))))))))(#1#(do()(())(p(read i)0)(terpri)))))

เข้าใกล้

แทนที่จะทำเหมือนคนอื่นและนับวงเล็บด้วยมือเราจะเรียกผู้อ่านเสียงกระเพื่อมและทำมันให้ถูกวิธี :-)

  • อ่านจากอินพุตสตรีมและเขียนไปยังสตรีมเอาต์พุตชั่วคราว
  • ในขณะที่ทำเพื่อให้ตัวละครที่แตกต่างจากการรวม(, )หรือช่องว่างเป็นสตริง
  • เอาท์พุทกลางจะใช้ในการสร้างสตริงซึ่งมีรูปแบบ Common-Lisp รูปแบบที่ดี syntactically: รายการที่ซ้อนกันของสตริง
  • ใช้สตริงนั้นเป็นสตรีมอินพุตเรียกใช้readฟังก์ชันมาตรฐานเพื่อสร้างรายการจริง
  • เรียกpแต่ละรายการเหล่านั้นซึ่งเขียนซ้ำไปยังเอาต์พุตมาตรฐานด้วยรูปแบบที่ร้องขอ โดยเฉพาะอย่างยิ่งสตริงจะถูกพิมพ์ไม่ได้ยกมา

อันเป็นผลมาจากวิธีนี้:

  1. รูปแบบการป้อนข้อมูลมีข้อ จำกัด น้อยกว่า: คุณสามารถอ่านอินพุตที่จัดรูปแบบโดยพลการไม่เพียง "หนึ่งฟังก์ชันต่อบรรทัด" (ugh)
  2. นอกจากนี้หากอินพุตไม่ได้รับการจัดรูปแบบที่ดีข้อผิดพลาดจะถูกส่งสัญญาณ
  3. ในที่สุดฟังก์ชั่นการพิมพ์ที่สวยนั้นแยกออกจากการแยกวิเคราะห์ได้อย่างง่ายดาย: คุณสามารถสลับไปยังวิธีการพิมพ์ S-expressions อีกวิธีหนึ่งได้อย่างง่ายดาย (และคุณควรถ้าคุณให้ความสำคัญกับพื้นที่แนวตั้ง)

ตัวอย่าง

อ่านจากไฟล์โดยใช้ wrapper นี้:

(with-open-file (*standard-input* #P"path/to/example/file")
    ...)

นี่คือผลลัพธ์:

(!@#$%^&*
    (asdfghjklm
        (this_string_is_particularly_long
            (...))
        (123456789)))
(THIS_IS_TOP_LEVEL_AGAIN
    (HERE'S_AN_ARGUMENT))
(-:0
    (*:0
        (%:0
            (Arg:6)
            (Write:0
                (Read:0
                    (Arg:30))
                (Write:0
                    (Const:-6)
                    (Arg:10))))
        (%:0
            (Const:9)
            (/:0
                (Const:-13)
                (%:0
                    (Arg:14)
                    (Arg:0)))))
    (WriteArg:22
        (-:0
            (Const:45)
            (?:0
                (Arg:3)
                (Arg:22)
                (Arg:0)))))

(ดูเหมือนว่าแท็บจะถูกแปลงเป็นช่องว่างที่นี่)

สวย - พิมพ์ (เวอร์ชั่น golfed)

ตรงกันข้ามกับเวอร์ชั่นดั้งเดิมที่ปลอดภัยกว่าเราคาดหวังว่าอินพุตจะถูกต้อง

(labels ((p (x d)
           (or
            (when (listp x)
              (princ #\()
              (p (car x) d)
              (incf d)
              (dolist (a (cdr x)) (format t "~%~v{  ~}" d '(t)) (p a d))
              (princ #\)))
            (princ x))))
  (let ((i
         (make-string-input-stream
          (with-output-to-string (o)
            (ignore-errors
             (do (b
                  c)
                 (nil)
               (if (member (setq c (read-char)) '(#\( #\) #\  #\tab #\newline)
                           :test 'char=)
                   (progn
                    (when b (prin1 (coerce (reverse b) 'string) o))
                    (princ c o)
                    (setq b nil))
                   (push c b))))))))
    (ignore-errors (do () (nil) (p (read i) 0) (terpri)))))

7

เรติน่า , 89 83 ไบต์

s`.+
$0<tab>$0
s`(?<=<tab>.*).
<tab>
+ms`^((\()|(?<-2>\))|[^)])+^(?=\(.*^((?<-2><tab>)+))
$0$3
<tab>+$
<empty>

โดย<tab>ย่อมาจากอักขระแท็บจริง (0x09) และ<empty>ย่อมาจากบรรทัดว่าง หลังจากทำการแทนที่คุณสามารถเรียกใช้โค้ดด้านบนด้วยการ-sตั้งค่าสถานะ อย่างไรก็ตามฉันไม่นับแฟล็กนั้นเพราะคุณสามารถใส่แต่ละบรรทัดลงในไฟล์ต้นฉบับของตัวเองได้ซึ่งในกรณีนี้ 7 บรรทัดใหม่จะถูกแทนที่ด้วยบทลงโทษ 7 ไบต์สำหรับไฟล์ต้นฉบับเพิ่มเติม

นี่เป็นโปรแกรมเต็มรูปแบบรับอินพุตบน STDIN และพิมพ์ผลลัพธ์ไปที่ STDOUT

คำอธิบาย

ทุกคู่ของบรรทัดจะกำหนดการทดแทน regex ความคิดพื้นฐานที่จะทำให้การใช้งานของ .NET ของกลุ่มสมดุลที่จะนับความลึกในปัจจุบันถึงที่กำหนดแล้วใส่ที่แท็บจำนวนมากก่อนหน้านั้น((

s`.+
$0<tab>$0

ก่อนอื่นเราเตรียมอินพุต เราไม่สามารถเขียนจำนวนแท็บที่มีเงื่อนไขได้หากเราไม่พบแท็บเหล่านั้นที่ใดก็ได้ในสตริงอินพุตเพื่อจับภาพ ดังนั้นเราเริ่มต้นด้วยการทำซ้ำอินพุตทั้งหมดคั่นด้วยแท็บ โปรดทราบว่าs`เพียงแค่เปิดใช้งานโมดิฟายเออร์แบบบรรทัดเดียว (หรือ "dot-all") ซึ่งทำให้แน่ใจได้ว่า.ยังตรงกับบรรทัดใหม่

s`(?<=<tab>.*).
<tab>

ตอนนี้เราเปลี่ยนทุกตัวอักษรหลังจากแท็บนั้นเป็นแท็บเช่นกัน สิ่งนี้ทำให้เรามีแท็บจำนวนมากพอที่ส่วนท้ายของสตริงโดยไม่ต้องแก้ไขสตริงต้นฉบับจนถึงตอนนี้

+ms`^((\()|(?<-2>\))|[^)])+^(?=\(.*^((?<-2><tab>)+))
$0$3

นี่คือเนื้อของการแก้ปัญหา mและsเปิดใช้งานโหมดหลายคู่สาย (เพื่อให้^ตรงกับจุดเริ่มต้นของเส้น) และโหมดบรรทัดเดียว ตัว+บอกให้ Retina ทำซ้ำการทดแทนนี้ต่อไปจนกว่าผลลัพธ์จะหยุดการเปลี่ยนแปลง (ในกรณีนี้หมายความว่าจนกว่ารูปแบบจะไม่ตรงกับสตริงอีกต่อไป)

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

((\()|(?<-2>\))|[^)])+

มันจับคู่กับ a (, ผลักมันลงบน2สแต็กหรือจับคู่), popping จับภาพล่าสุดจาก2สแต็กหรือตรงกับสิ่งอื่นและปล่อยให้สแต็กไม่มีใครแตะต้อง เนื่องจากวงเล็บรับประกันว่ามีความสมดุลเราจึงไม่ต้องกังวลเกี่ยวกับการพยายามที่จะโผล่ขึ้นมาจากกองที่ว่างเปล่า

หลังจากที่เราได้ผ่านสายเช่นนี้และพบว่ายังไม่ได้(หยุดที่ lookahead จากนั้นข้ามไปยังจุดสิ้นสุดของสตริงและจับแท็บเป็นกลุ่ม3ในขณะที่ popping จาก2กองจนกว่าจะว่างเปล่า:

(?=\(.*^((?<-2><tab>)+))

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

<tab>+$
<empty>

สุดท้ายเราก็กำจัดแท็บตัวช่วยที่ท้ายสตริงเพื่อล้างผลลัพธ์


นี่มันเจ๋งมาก ทำได้ดี! เป็นความยินดีเสมอที่ได้เห็น Retina
BrainSteel

6

C: 95 94 ตัวอักษร

มันยังไม่ค่อยออกกอล์ฟและจากคำถามฉันไม่แน่ใจว่าแท็บนั้นยอมรับได้หรือไม่ซึ่งเป็นสิ่งที่ฉันใช้ที่นี่

i,j;main(c){for(;putchar(c=getchar()),c+1;i+=c==40,i-=c==41)if(c==10)for(j=i;j--;putchar(9));}

Ungolfed:

i,j;
main(c){
  for(
    ;
    putchar(c=getchar()),
    c+1;
    i+=c==40,
    i-=c==41
  )
    if(c==10)
      for(
        j=i;
        j--;
        putchar(9)
      );
}

แก้ไข: สร้างขึ้นเพื่อให้ออกจาก EOF


แท็บเป็นที่ยอมรับอย่างสมบูรณ์
BrainSteel

2
คุณสามารถใช้if(c<11)แทนif(c==10)?
Digital Trauma

5

Julia, 103 99 97 94 88 ไบต์

p->(i=j=0;for l=split(p,"\n") i+=1;println("\t"^abs(i-j-1)*l);j+=count(i->i=='\)',l)end)

ฟังก์ชันนี้กำหนดฟังก์ชันที่ไม่มีชื่อที่ยอมรับสตริงและพิมพ์เวอร์ชันที่เยื้อง หากต้องการเรียกใช้ให้ตั้งชื่อเช่นf=p->...เรียกว่าให้มันชื่อเช่นโปรดทราบว่าการป้อนข้อมูลจะต้องเป็นสตริง Julia ที่ถูกต้องดังนั้นสัญญาณดอลลาร์ ( $) จะต้องหนีออกมา

คำอธิบาย Ungolfed +:

function f(p)
    # Set counters for the line number and the number of close parens
    i = j = 0

    # Loop over each line of the program
    for l in split(p, "\n")
        # Increment the line number
        i += 1

        # Print the program line with |i-j-1| tabs
        println("\t"^abs(i-j-1) * l)

        # Count the number of close parens on this line
        j += count(i -> i == '\)', l)
    end
end

ตัวอย่างการทำท่าช่องว่างสี่ชุดแต่ละชุดคือ:

julia> f("(A
(B
(C)
(D))
(E))")

(A
    (B
        (C)
        (D))
    (E))

คำแนะนำใด ๆ มีมากกว่ายินดีต้อนรับ!



3

Perl, 41

$_="\t"x($i-$j).$_;$i+=y/(/(/;$j+=y/)/)/

40ตัวอักษรสำหรับ+1-p

ทำงานด้วย:

cat input.txt | perl -pe'$_="\t"x($i-$j).$_;$i+=y/(/(/;$j+=y/)/)/'

3

Python 2 - 88 78 Bytes

วิธีการแก้ปัญหาที่ตรงไปตรงมา (และไม่สั้นมาก):

l=0
for x in raw_input().split():g=x.count;d=l*'\t'+x;l+=g("(")-g(")");print d

เคล็ดลับสองสามข้อ: 1) คุณสามารถใช้'\t'แทน' 'และบันทึกหนึ่งไบต์ 2) ไม่จำเป็นต้องกำหนดให้input.split()กับตัวแปรเนื่องจากมันจะใช้เพียงครั้งเดียว (เหมือนกันสำหรับcเช่นเดียวกับ - dเพียงแค่ย้ายprintคำสั่ง); 3) ลำดับความสำคัญของโอเปอเรเตอร์หมายถึงl*cไม่จำเป็นต้องใช้วงเล็บ นอกจากนี้ดูเหมือนfว่าไม่ได้ใช้สำหรับสิ่งใด - เป็นของที่ระลึกรุ่นก่อนหน้าหรือไม่
DLosc

นอกจากนี้หากนี่คือ Python 2 คุณจะต้องใช้raw_inputแทนinput(และอย่าลืมวงเล็บหลังจากนั้น!)
DLosc

2

CJam, 20 ไบต์

r{_')e=NU)@-:U9c*r}h

ลองใช้ออนไลน์ในล่าม CJamล่าม

มันทำงานอย่างไร

r                    e# Read a whitespace-separated token R from STDIN.
{                 }h e# Do, while R is truthy: 
  _')e=              e#   Push C, the number of right parentheses in R. 
       NU            e#   Push a linefeed and U (initially 0).
         )@-         e#   Compute U + 1 - C.
            :U       e#   Save in U.
              9c*    e#   Push a string of U tabulators.
                 r   e#   Read a whitespace-separated token R from STDIN.
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.