Lisp benzeri bir Sözdizimi Biçimlendirme


23

Arka fon

(Gerçek bir kalp anahtarlama hikayesine dayanarak)

Benim zamanımda Lisp ve benzeri dillerle sık sık oynadım. Onlarla yazdım, onları koştum, yorumladım, tasarladım ve makinelerin benim için yazmasını sağladım ... Ve eğer beni rahatsız eden bir şey varsa, benim özel biçimlendirme stilime uymayan Lisp'i görüyor.

Maalesef, bazı metin editörleri ( öksürük XCode öksürüğü ), kod kopyalanıp yapıştırıldığında güzel sekmelerimi ve boşluklarımı çıkarmaya meyilli ... Bu güzel aralıklı Lisp benzeri sözdizimini kullanın:

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

( ABCDEKeyfi fonksiyonlar nerede )

BAZI metin editörleri bu güzel kodu aşağıdaki amaca yönlendirir:

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

Ne dağınıklık! Bu okunabilir değil!

Bana yardım et, burada mı?

Meydan okuma

Bu zorluğun amacı, aşağıda açıklanan biçimde yeni satırlarla ayrılmış bir dizi işlevi almak ve okunabilirliği ve şıklığı vurgulayan daha güzel bir düzenleme döndürmektir.

Girdi

FArity Nargümanlarının fonksiyonunu , aşağıdakine benzer bir yapı olarak tanımlarız :

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

nerede G1, G2, ..., GNve kendilerini tüm fonksiyonlar bulunmaktadır. Bir aritelik 0işlevi Abasitçe (A)bir aritelik 2işlevi Bbiçimindedir(B (...) (...))

Kodunuz, her bir işlevin parantezinden önce (ilk işlev hariç), tek satırlı yeni bir işlev dizisi olarak girdi almalıdır. Yukarıdaki örnek geçerli girdidir.

Şöyle kabul edebilirsiniz:

  • Parantezler dengelidir.
  • Bir fonksiyonun 250 kezden daha fazla girintili olması gerekmez.
  • HER işlev parantezlerle çevrilidir: ()
  • Bir fonksiyonun adı sadece yazdırılabilir ASCII karakterleri içerecektir.
  • Bir işlevin adı hiçbir zaman parantez veya boşluk içermez.
  • Girişte isteğe bağlı son bir satır var.

Çıktı

Kodunuz , yapılan tek değişikliklerin, işlevlerin önde gelen parantezlerinden önceki boşluk veya sekmelerin eklenmesi olduğu aynı işlev kümesini çıkarmalıdır . Çıktı aşağıdaki kurallara uymalıdır:

  • Verilen ilk işlev (ve daha sonra en üst düzey işlevler) önünde boşluk olmamalıdır
  • Bir işlevin yatay konumuna ilişkin bir argüman, bu işlevin yatay konumunun sağındaki tam olarak bir sekmedir.
  • Bir sekme uygulama tanımlıdır, ancak en az 3 boşluk olmalıdır.
  • İsteğe bağlı olarak, her satırdan sonra en fazla iki alan yazdırabilirsiniz.

kurallar

Örnekler

Giriş:

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

Çıktı:

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

Giriş:

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

Çıktı:

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

Giriş:

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

Çıktı:

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

Sıcak Ağ Soruları listesini yaptığınız için tebrikler! : D
Alex A.

@AlexA. Yaşasın! Rüyalarım gerçekleşti. : D
BrainSteel

Peki ya fonksiyon ismi yoksa, ne olur ()?
coredump

Girinti> 3 boşluk mu olmalı yoksa bir sekme kabul edilebilir mi?
isaacg

@ isaacg Bu durumda tüm fonksiyonların isimlendirildiğini varsayabilirsiniz. Ve işletim sisteminizin / dilinizin yatay bir sekme olarak tanımladığı şey iyi. Boşluk kullanıyorsanız, en az 3 olmalıdır. Bir bilgisayara ne zaman girebileceğimi açıklığa kavuştururum. Teşekkürler!
BrainSteel

Yanıtlar:


9

Pyth, 24 20 19 18 bayt

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

Her satır için bir sayaç artırır, şu ana kadar karşılaşılan toplam kapanış parantez sayısını sayar ve sayaçtan çıkarır. Sonra countersekmelere göre girintileriz .


@Aşağıdakileri açıklamak ister misiniz?
orlp

Oy düşürmedim, ancak *4kodlanmış ve gereksiz bir tercih. FN.z+*ZC9N~Z-1/N\)editörünüzün girinti genişliğini kullanmanıza izin verir ve bir bayt tasarrufu sağlar.
Cees Timmerman

Kabul ediyorum, bir sekme bir karakter daha kısa olurdu. \<tab>veya C9.
isaacg

9

Yaygın Lisp - 486 414 bayt (Rube Goldberg versiyonu)

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

yaklaşım

Herkes gibi yapmak ve parantezleri elle saymak yerine, Lisp okuyucuyu çağıralım ve Doğru Yolla :-) yapalım.

  • Giriş akışından okuyun ve geçici bir çıkış akışına yazın.
  • Bunu yaparken, toplam karakterlerden farklı ( , )dizeleri olarak veya boşluk.
  • Ara çıktı, sözdizimsel olarak iyi oluşturulmuş Common-Lisp formları içeren bir dize oluşturmak için kullanılır: dizelerin iç içe geçmiş listeleri.
  • Bu dizeyi bir giriş akışı olarak kullanarak, standardı çağırın read gerçek listeler oluşturmak işlevi .
  • pTekrarlanan bir şekilde standart çıktıya istenen biçimde yazılan bu listelerin her birini çağırın . Özel olarak, dizeler tırnak işaretleri olmadan yazdırılır.

Bu yaklaşımın bir sonucu olarak:

  1. Giriş biçiminde daha az kısıtlama vardır: yalnızca "her satırda bir işlev" yerine, rasgele biçimlendirilmiş girdileri okuyabilirsiniz.
  2. Ayrıca, giriş iyi oluşturulmuş değilse, bir hata bildirilecektir.
  3. Son olarak, güzel baskı işlevi ayrıştırma işleminden iyi bir şekilde ayrılır: kolayca güzel S ifadeleri basma yöntemine geçebilirsiniz (ve dikey alanınıza değer veriyorsanız bunu yapmanız gerekir).

Örnek

Bir dosyadan okumak, bu sarıcıyı kullanarak:

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

İşte sonuç:

(!@#$%^&*
    (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)))))

(sekmeler burada boşluklara dönüştürülmüş gibi görünüyor)

Oldukça basılmış (golf versiyonu)

Daha güvenli orijinal versiyonun aksine, girişin geçerli olmasını bekliyoruz.

(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

Retina , 89 83 bayt

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

Burada <tab>gerçek bir sekme karakteri (0x09) ve <empty>boş bir satır anlamına gelir. Bu değişiklikleri yaptıktan sonra yukarıdaki kodu-s bayrakla . Ancak, bu bayrağı saymıyorum, çünkü her satırı kendi kaynak dosyasına da koyabilirsiniz, bu durumda 7 yeni satır ek kaynak dosyaları için 7 ceza baytı ile değiştirilir.

Bu tam bir programdır, STDIN'e giriş alarak sonucu STDOUT'a yazdırır.

açıklama

Her satır çifti bir regex ikamesini tanımlar. Temel fikir, mevcut derinliği belirli bir sayıya kadar saymak için .NET'in dengeleme gruplarını kullanmak (ve daha önce bu sekmeleri eklemek (.

s`.+
$0<tab>$0

İlk önce girişi hazırlıyoruz. Onları yakalamak için giriş dizesinde bir yerde bulamazsak, koşullu sayıda sekmeyi gerçekten geri yazamayız. Böylece girişin tamamını bir sekmeyle ayırarak çoğaltarak başlıyoruz. Not o s`sadece tek satır aktive (veya "nokta-all") değiştirici, olmasını sağlar .ayrıca yeni satır eşleşir.

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

Şimdi bu sekmeden sonraki her karakteri bir sekmeye de çeviriyoruz. Bu bize şimdiye kadar orijinal dizeyi değiştirmeden dizenin sonunda yeterli miktarda sekme verir.

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

Bu, çözümün etidir. mVe sçok hatlı modunda (ki aktif ^çizgilerinin başlangıçlarını eşleşir) ve tek-hat moduna. +Çıktı değişen durana kadar (bu durumda araçlar desen artık dizeyle eşleşir kadar bu,) bu ikame tekrar tutmak için Retina söyler.

Model, girişin işlenmemiş bir önekiyle eşleşir ((yani, (ondan önce sekme olmayan, ancak olmalıdır). Aynı zamanda, yığının yüksekliği 2mevcut derinliğe ve dolayısıyla eklememiz gereken sekme sayısına karşılık gelecek şekilde dengeleme gruplarıyla önek derinliğini belirler . Bu kısım:

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

Ya a ile eşleşir ya da a ile eşleşir ya da son yakalamayı yığından (fırlatır ya da başka bir şeyle eşleşir ve yığını el değmeden bırakır. Parantezlerin dengeli olması garanti edildiğinden, boş bir yığından çıkmaya çalışmaktan endişe etmemize gerek yoktur.2)2

Böyle bir ipten geçtikten ve (durdurulan bir işlenmemiş bulduktan sonra, bakış açısı daha sonra dizenin sonuna atlar 3ve 2istif boşalıncaya kadar yığından fırlarken sekmeleri grup halinde yakalar :

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

İçeride bir +içeriğin kullanılmasıyla, desenin eşleşmeye en az bir sekme eklenmesi gerektiğinde her şeyle eşleşmesini sağlıyoruz; bu, çoklu kök seviyeli işlevler olduğunda sonsuz bir döngüyü önler.

<tab>+$
<empty>

Son olarak, sonucu temizlemek için dizenin sonundaki yardımcı sekmelerden kurtulduk.


Bu çok havalı. Aferin! Retina'yı görmek her zaman bir zevktir.
BrainSteel

6

C: 95 94 karakter

Henüz pek golf sahası olmadı ve soruya göre sekmelerin kabul edilebilir olup olmadığından emin değilim.

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

Düzenleme: EOF üzerinde çıkmak için yapılmıştır.


Sekmeler tamamen kabul edilebilir.
BrainSteel

2
Eğer kullanabilir misiniz if(c<11)yerine if(c==10)?
Dijital Travma

5

Julia, 103 99 97 94 88 bayt

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

Bu, bir dizgeyi kabul eden ve girintili sürümü basan adsız bir işlevi tanımlar. Aramak için bir isim verin, örneğin f=p->.... Girişin geçerli bir Julia dizesi olması gerektiğine dikkat edin, bu yüzden dolar işareti ( $) kaçmalıdır.

Ungolfed + açıklama:

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

Örnek olarak, her dört boşluk kümesini göstermek, bir sekmedir:

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

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

Herhangi bir öneri kabul edilir!


4

Haskell, 83 81

unlines.(scanl(\n s->drop(sum[1|')'<-s])$n++['\t'|'('<-s])"">>=zipWith(++)).lines

çok puansız bir çözüm.


sadece düşebilirsin h=.
Will Ness

3

Perl, 41

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

40karakterler +1için-p .

Çalıştır:

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

3

Python 2 - 88 78 Bayt

Oldukça basit (ve çok kısa değil) bir çözüm:

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

Birkaç ipucu: 1) Bir bayt '\t'yerine kullanabilir ' 've kaydedebilirsiniz; 2) gerek atamak için input.split()yalnızca aynı (bir kez kullanılan beri, bir değişkene cyanı sıra, d--just hareket print) deyimi; 3) Operatör önceliği etrafındaki parantezlere l*cgerek olmadığı anlamına gelir . Ayrıca, fhiçbir şey için kullanılmamış gibi görünüyor - bu önceki bir sürümün kalıntısı mı?
DLosc

Ayrıca, eğer bu Python 2 ise, raw_inputyerine kullanmanız gerekir input(ve bundan sonra parantezleri de unutmayın!).
DLosc

2

CJam, 20 bayt

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

CJam tercümanında çevrimiçi olarak deneyin .

Nasıl çalışır

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.
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.