challenge コード圧縮

スペースやインデントなど、本来は必要なく開発効率を上げるために記述が許可されている項目について、
それらを可能な限り減らし、コードを短くするコード書いてください。
また、投稿時に対象とする言語と、実際の処理結果を記載できるとわかり易いかと思います。

以下詳細
・全てを行う必要はありません、どこまで行うかは任意です。
・ローカル宣言など、消しても動作に関係のない構文の削除や置き換えを行っても構いません。
・必ず同じ入力に同じ結果が返るのであれば処理内容を変えることもかまいませんが、推奨・強制はしません。
・コンパイラや実行環境に依存する圧縮は避けてください。

Posted feedbacks - Nested

Flatten Hidden
こんな感じでしょうか
$ runhaskell HaskellParser.hs < HaskellParser.hs
module Main where { import Language.Haskell.Parser; import Language.Haskell.Pretty; import Language.Haskell.Syntax; import Text.PrettyPrint.HughesPJ; main = do { (ParseOk x) <- return . parseModule =<< getContents; putStrLn $ prettyPrintStyleMode (Style OneLineMode maxBound 0) defaultMode{layout = PPSemiColon} x}}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
module Main where                                                                                                       

import Language.Haskell.Parser
import Language.Haskell.Pretty
import Language.Haskell.Syntax
import Text.PrettyPrint.HughesPJ

main = do
  (ParseOk x) <- return . parseModule =<< getContents
  putStrLn $ prettyPrintStyleMode (Style OneLineMode maxBound 0) defaultMode {layout = PPSemiColon} x

Squeak Smalltalk で。

1
2
3
4
5
6
7
| method |
method := Integer >> #factorial.
^method methodClass prettyPrinterClass
    format: method getSource
    in: method methodClass
    notifying: nil
    decorated: false.
すごいマイナス評価・・・!

Rのdeparse()は、適切にインデントを入れなおしてくれるという
(このお題的には)余計な機能がついてますが、とりあえずこんな感じで。

> writeLines(deparse(parse(text=code, srcfile=NULL)))
expression(sapply(1:10, function(n) {
    for (i in 1:n) {
        print(n)
    }
    print("------")
}))
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
code <- ' sapply(1:10, function(n)
                      {
                          for(i in 1:n)
  {
                              print(n)
                               }
                          print("------")
                                      }
# comment
         )
'
writeLines(deparse(parse(text=code, srcfile=NULL)))

:D

1
import this
4番目の条件がなければ、
私はプラス評価しますよ(やらないけど)。

「コンパイラが構文解析した結果を利用する」
なら楽しそうじゃないですか。

Gauche で書いてみました。基本的に read して write しただけです。

1
2
3
4
(define (main args)
  (for-each write
            (call-with-input-file (cadr args)
              (cut port->list read <>))))
1
2
<?php
echo php_strip_whitespace("filename.php");
C#でまじめにやってみた。
(あれ?α置換でも同じようなコード書いた気が)
動けばよかろう、な品質です。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
using System;
using System.Text;
using System.IO;

namespace DouKaku
{
    class Program
    {
        /**
         * 内部状態
         *   通常,文字リテラル,文字列リテラル,逐語文字列リテラル,行コメント,範囲コメント
         */
        enum State { None, Char, RegularStr, VerbatimStr, LineComment, DelimitedComment }

        /* コード圧縮を行う */
        static string Compress(string code)
        {
            string WhiteSpace = " \t\r\n";                  //空白
            string LineTerminator = "\r\n";                 //行終端
            string Kigou = @"{}[]().,:;+-*/%&|^!~=<>?""'@"; //演算子・記号
            StringBuilder sb = new StringBuilder();
            State state = State.None;

            for (int i = 0 ; i < code.Length ; i++) {
                switch (state) {
                case State.LineComment:         //行コメント
                    if (LineTerminator.IndexOf(code[i]) >= 0)
                        state = State.None;
                    break;
                case State.DelimitedComment:    //範囲コメント
                    if (code[i] == '*' && code[i + 1] == '/') {
                        state = State.None;
                        i++;
                    }
                    break;
                case State.RegularStr:          //文字列リテラル
                case State.Char:                //文字リテラル
                    if (code[i] == '\\')
                        sb.Append(code[i++]);
                    else if (code[i] == (state == State.Char ? '\'' : '"'))
                        state = State.None;
                    sb.Append(code[i]);
                    break;
                case State.VerbatimStr:         //逐語文字列リテラル
                    if (code[i] == '"')
                        if (code[i + 1] != '"')
                            state = State.None;
                        else {
                            sb.Append('"');
                            i++;
                        }
                    sb.Append(code[i]);
                    break;
                case State.None:                //通常
                    if (code[i] == '/' && code[i + 1] == '/') {
                        state = State.LineComment;
                    } else if (code[i] == '/' && code[i + 1] == '*') {
                        state = State.DelimitedComment;
                        i++;
                    } else if (code[i] == '"') {
                        state = State.RegularStr;
                        sb.Append(code[i]);
                    } else if (code[i] == '\'') {
                        state = State.Char;
                        sb.Append(code[i]);
                    } else if (code[i] == '@' && code[i + 1] == '"') {
                        state = State.VerbatimStr;
                        sb.Append("@\"");
                        i++;
                    } else if (WhiteSpace.IndexOf(code[i]) >= 0) {
                        if (WhiteSpace.IndexOf(code[i + 1]) < 0
                         && Kigou.IndexOf(code[i + 1]) < 0
                         && 0 < sb.Length
                         && WhiteSpace.IndexOf(sb[sb.Length - 1]) < 0
                         && Kigou.IndexOf(sb[sb.Length - 1]) < 0)
                            sb.Append(' ');
                    } else
                        sb.Append(code[i]);
                    break;
                }
            }
            return sb.ToString();
        }

        static void Main(string[] args)
        {
            try {
                string input = File.ReadAllText(args[0]);
                string output = Compress(input);
                Console.WriteLine(output);
            } catch (ApplicationException ex) {
                Console.WriteLine(ex.StackTrace);
            }
        }
    }
}

Postscript では、通常、コードは実行可能な圧縮配列の形で current dict に記録されるので、それを書きだす、という方向 でいいのでしょうかね.... あまり潰しが利きませんが...

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
%!PS

% テスト用コード
/TestProc { % Hogehoge Fugafuga
    1 3 roll 10 add % areare
    (abc%Hoge) == == % zzz
} def

% ========  出力
currentdict /TestProc get ==

% ======== 以下出力結果
% {1 3 roll 10 add (abc%Hoge) == ==}

コメント #6548 を改造して、全てのコメントを除いて空白と改行を1個にまとめるプログラムにしました。

空白は削除できる場合もあるのですが、意味が変わる場合が考えられるために1個は残してあります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import java.util.regex.*;
import java.io.*;

public class Decomment2 {
    static final String LITERAL = "(?:\"(?:\\\\.|[^\"])*\")|(?:\'(?:\\\\.|[^\'])*\')";
    static final String COMMENT1 = "(?s:/\\*.*?\\*/)";
    static final String COMMENT2 = "//.*";
    static final String SPACES = "\\s+";
    static final Pattern DECOM_PAT = Pattern.compile("(" + LITERAL + ")|((" + 
            COMMENT1 + "|" + COMMENT2 + "|" + SPACES + ")+)");
    static final String LINE_SEPARATOR = System.getProperty("line.separator");
    
    public static void main(String[] args) throws IOException {
        for (String name : args) {
            BufferedReader r = new BufferedReader(new FileReader(name));
            FileWriter w = new FileWriter(name + ".out");
            StringBuilder sb = new StringBuilder();
            String line;
            while ((line = r.readLine()) != null) {
                sb.append(line).append(LINE_SEPARATOR);
            }
            Matcher m = DECOM_PAT.matcher(sb);
            StringBuffer replace = new StringBuffer();
            while (m.find()) {
                if (m.group(2) != null && m.group(2).length() > 0) {
                    m.appendReplacement(replace, " ");
                } else {
                    m.appendReplacement(replace, "$1");
                }
            }
            m.appendTail(replace);
            w.write(replace.toString());
            w.close();
        }
    }
}
readで読んで、writeでプリティプリント属性をオフにして印字してみています。
入力ファイル:
;;; doukaku-189.data -*- lisp -*- 

;; 1
(let ((x 10   )
      (y 20  ))
  (list x  y  ))

;; 2
(let ((x 10)
      (y   20))
  (list x  y   ))
~~
印字結果:
(LET ((X 10) (Y 20)) (LIST X Y))(LET ((X 10) (Y 20)) (LIST X Y))
1
2
3
(with-open-file (str "doukaku-189.data")
  (loop :for in := (read str nil nil) :while in 
        :do (write in :pretty nil)))

昔ゴルフ用に書いたものを書き直しました。Emacs のバッファを書き換えます。対象言語は Common Lisp で、バッファのモードは lisp-mode と仮定しています。

出力例:
(defun mylib-compress-need-space-p()(cond((or(bobp)(eobp))nil)((string-match"["'(),`]"(string(following-char)))nil)((looking-back"#[0-9]+#")nil)((looking-back"#\\.")t)((string-match"["()]"(string(preceding-char)))nil)(t)))
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
(defun mylib-compress-region (&optional start end)
  (interactive "r")
  (save-restriction
    (when (and start end)
      (narrow-to-region start end)
      (goto-char (point-min)))
    (loop
      do (delete-region (point)
                        (progn (while (forward-comment 1)) (point)))
      until (or (eobp) (= (following-char) ?\)))
      if (mylib-compress-need-space-p)
      do (insert " ")
      if (looking-at "\\(#'?\\|['`]\\|,@?\\)?(")
      do
      (down-list 1)
      (mylib-compress-region)
      (up-list 1)
      else do (forward-sexp 1))))

(defun mylib-compress-need-space-p ()
  (cond ((or (bobp) (eobp))
         nil)
        ((string-match "[\"'(),`]" (string (following-char)))
         nil)
        ((looking-back "#[0-9]+#")
         nil)
        ((looking-back "#\\\\.")
         t)
        ((string-match "[\"()]" (string (preceding-char)))
         nil)
        (t)))

Index

Feed

Other

Link

Pathtraq

loading...