Lispのような構文のフォーマット


23

バックグラウンド

(真実の、心が痛む物語に基づいて)

私の時間の中で、私はLispや同様の言語でよく遊んでいました。私はそれらで書いて、実行して、解釈して、設計して、マシンに私のために書いてもらいました...そして、もし私を悩ますことがあるなら、それは私の特定のフォーマットスタイルに準拠していないLispを見ています。

残念ながら、一部のテキストエディター( XCode )は、コードをコピーして貼り付けるたびに美しいタブとスペースを削除する傾向があります。

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

ABCDE任意の関数はどこにありますか)

一部のテキストエディターは、この素敵なコードを次の目的で処理します。

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

なんてこった!それは読めません!

ここで私を助けて?

チャレンジ

この課題の目標は、以下で説明する形式で改行で区切られた一連の関数を取得し、読みやすさと優雅さを強調するより美しい配置を返すことです。

入力

FアリティN引数の関数を、次のような構成体として定義します。

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

G1, G2, ..., GNすべての機能はそれ自体でどこにありますか。アリティ0関数Aは単純(A)ですが、アリティ2関数Bの形式は(B (...) (...))

コードは、すべての関数の先頭の括弧(最初の関数を除く)の前に、単一の改行を持つ一連の関数として入力を受け取る必要があります。上記の例は有効な入力です。

あなたは仮定することができます:

  • 括弧はバランスが取れています。
  • 関数を250回以上インデントする必要はありません。
  • すべての機能は括弧で囲まれています。 ()
  • 関数の名前には、印刷可能なASCII文字のみが含まれます。
  • 関数の名前に括弧やスペースが含まれることはありません。
  • 入力時にオプションの末尾の改行があります。

出力

コードは同じ関数セットを出力する必要がありますが、変更されるのは、関数の先頭の括弧の前にスペースまたはタブを追加することだけです。出力は次のルールに準拠する必要があります。

  • 指定された最初の関数(およびその後のトップレベル関数)には、先行スペースを含めないでください
  • 関数の水平位置の引数は、その関数の水平位置の右側にあるタブ1つです。
  • タブは実装定義ですが、少なくとも3つのスペースが必要です。
  • オプションで、各行の後に最大2つのスペースを印刷できます。

ルール

  • これはコードゴルフです:最短のコードが勝ちます!
  • 標準の抜け穴は許可されていません。

入力:

(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
アレックスA.

@AlexA。やった!私の夢が実現しました。:D
BrainSteel

次のような関数名がない場合はどうなり()ますか?
コアダンプ

インデントは3つ以上のスペースである必要がありますか、それともタブは受け入れられますか?
isaacg

@isaacgこの場合、すべての関数に名前が付けられていると想定できます。また、OS /言語が水平タブとして定義しているものは何でも構いません。スペースを使用する場合は、少なくとも3つある必要があります。いつコンピューターにアクセスできるかを明確にします。ありがとう!
BrainSteel

回答:


9

Pyth、24 20 19 18バイト

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

行ごとにカウンターをインクリメントし、これまでに発生した閉じ括弧の総数をカウントし、カウンターから減算します。次に、counterタブでインデントします。


説明する@Downvoterケア?
orlp

私はダウン投票しませんでしたが、これ*4はハードコーディングされた冗長な設定です。FN.z+*ZC9N~Z-1/N\)エディターのインデント幅を使用して、1バイトを節約できます。
シーズティマーマン

同意すると、タブは1文字短くなります。\<tab>またはC9
isaacg

9

Common Lisp- 486 414バイト(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)))))

アプローチ

他のみんなと同じように手で括弧を数える代わりに、Lispリーダーを呼び出して正しい方法でやってみましょう:-)

  • 入力ストリームから読み取り、一時出力ストリームに書き込みます。
  • そうすることが、集計の文字は異なる()または文字列として空白。
  • 中間出力は、構文的に整形式のCommon-Lispフォーム(ネストされた文字列のリスト)を含む文字列を作成するために使用されます。
  • その文字列を入力ストリームとして使用し、標準read関数を呼び出して実際のリストを作成します。
  • pこれらの各リストを呼び出して、要求された形式で標準出力に再帰的に書き込みます。特に、文字列は引用符なしで印刷されます。

このアプローチの結果として:

  1. 入力形式に対する制限が少なくなります。「1行に1つの関数」だけでなく、任意の形式の入力を読み取ることができます(ugh)。
  2. また、入力が整形式でない場合、エラーが通知されます。
  3. 最後に、pretty-printing関数は解析から十分に分離されています:S-expressionsをpretty-printingの別の方法に簡単に切り替えることができます(垂直スペースを重視する場合は、そうする必要があります)。

このラッパーを使用して、ファイルから読み取ります。

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

(タブはここでスペースに変換されるようです)

プリティプリント(ゴルフバージョン)

より安全な元のバージョンとは反対に、入力が有効であると予想されます。

(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に出力します。

説明

行の各ペアは、正規表現の置換を定義します。基本的な考え方は、.NETのバランシンググループを使用して、現在の深さを特定の数までカウントし、(その前にその数のタブを挿入すること(です。

s`.+
$0<tab>$0

まず、入力を準備します。入力文字列のどこかにタブをキャプチャできない場合、条件付きの数のタブを実際に書き戻すことはできません。そこで、入力全体をタブで区切って複製することから始めます。s`は、単一行(または「すべてをドット」)修飾子をアクティブにするだけであり、これにより.改行も一致することに注意してください。

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

次に、そのタブの後のすべての文字もタブに変えます。これにより、元の文字列を変更することなく、文字列の末尾に十分な量のタブが追加されます。

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

これがソリューションの要です。マルチラインモード(その結果、活性化線の始まりと一致する)とシングルラインモード。出力は(パターンは、もはや文字列に一致するまで、この場合にはその手段)を変更しない停止するまで、この置換を繰り返し続けるために網膜を伝えます。ms^+

パターン自体は、入力の接頭辞と未処理((つまり、(タブの前にタブはありませんが、その前にある)に一致します。同時に、スタックの高さが2現在の深さ、したがって追加する必要のあるタブの数に対応するように、バランシンググループでプレフィックスの深さを決定します。これがこの部分です。

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

a (に一致し、2スタックにプッシュするか、a に一致して、スタック)から最後のキャプチャをポップする2か、他の何かに一致してスタックをそのままにします。括弧のバランスが保証されているため、空のスタックからポップしようとすることを心配する必要はありません。

このような文字列を調べて(、停止する未処理を見つけた後、先読みは文字列の最後までスキップ3し、2スタックから空になるまでスタックからポップしながらタブをグループにキャプチャします。

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

+そこにa を使用することにより、少なくとも1つのタブを一致に挿入する必要がある場合にのみパターンが一致するようにします。これにより、複数のルートレベル関数がある場合の無限ループを回避します。

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

ゴルフをしていない:

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)か?
デジタル外傷

5

ジュリア、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->...ます。入力は有効なジュリア文字列で$なければならないため、ドル記号()をエスケープする必要があることに注意してください。

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

たとえば、4つのスペースの各セットのふりはタブです。

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

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

どんな提案でも歓迎です!


4

ハスケル、83 81

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

非常にポイントのないソリューション。


あなただけをドロップすることができますh=
ウィルネス

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バイト

かなり簡単な(そしてそれほど短くない)ソリューション:

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

いくつかのヒント:1)1バイトの'\t'代わりに使用' 'して保存できます。2)input.split()変数に1回しか使用されないため、変数に代入する必要はありません(とc同様に、-- djust 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.