1. pik

    Posted

    pik
Changes in title
+Delphi をワンライナー書けるっぽくする
Changes in tags
Changes in body
Source | HTML | Preview
@@ -0,0 +1,182 @@
+# Delphi のワンライナーとは
+
+勢いで「ワンライナー自慢大会」なんて Advent Calendar を作ったは良い物の、そもそも Delphi ではワンライナーらしいワンライナー書けなくない!?という…
+
+僕の中での「ワンライナーらしいワンライナー」は単純に1行で実行できる、という訳では無くコンソールから起動したいという思いがあって、そうするとコンパイル言語である Object Pascal はそれができない訳です。
+
+ということで、まずはコンソールからコンパイラを呼び出すプログラムを作りました。
+それが dcce です。
+
+# dcce
+
+ソースコードはこう!
+
+```pascal
+program dcce;
+
+{$APPTYPE CONSOLE}
+{$RTTI EXPLICIT METHODS([]) FIELDS([]) PROPERTIES([])}
+{$WEAKLINKRTTI ON}
+
+uses
+ System.SysUtils, System.IOUtils, Winapi.Windows;
+
+function GetReg(const iKey: HKEY; const iAddress, iValue: String): String;
+begin
+ var hReg: HKEY := 0;
+ var Ret := RegOpenKeyEx(iKey, PChar(iAddress), 0, KEY_QUERY_VALUE, hReg);
+ try
+ if Ret = ERROR_SUCCESS then
+ begin
+ SetLength(Result, $ff);
+ var RegType: DWORD := REG_SZ;
+ var VarSize: DWORD := Length(Result);
+
+ Ret :=
+ RegQueryValueEx(
+ hReg,
+ PChar(iValue),
+ nil,
+ @RegType,
+ PByte(PChar(Result)),
+ @VarSize
+ );
+
+ if Ret = ERROR_SUCCESS then
+ begin
+ SetLength(Result, VarSize div SizeOf(Char));
+ Result := Result.Trim;
+ end
+ else
+ Result := '';
+ end;
+ finally
+ RegCloseKey(hReg);
+ end;
+end;
+
+procedure Exec(const iCmd: String; const iNeedOutput: Boolean);
+var
+ SI: TStartUpInfo;
+ PI: TProcessInformation;
+begin
+ ZeroMemory(@SI, SizeOf(SI));
+ with SI do
+ begin
+ cb := SizeOf(SI);
+
+ if not iNeedOutput then
+ dwFlags := STARTF_USESTDHANDLES;
+ end;
+
+ if CreateProcess(nil, PChar(iCmd), nil, nil, True, 0, nil, nil, SI, PI) then
+ with PI do
+ try
+ while WaitForSingleObject(PI.hProcess, 100) = WAIT_TIMEOUT do
+ ;
+ finally
+ CloseHandle(hProcess);
+ CloseHandle(hThread);
+ end;
+end;
+
+begin
+ var DprPath := TPath.ChangeExtension(TPath.GetTempFileName, '.dpr');
+ var ExePath := TPath.ChangeExtension(DprPath, '.exe');
+ try
+ var Writer := TFile.CreateText(DprPath);
+ try
+ Writer.Write('begin ');
+
+ for var i := 1 to ParamCount do
+ Writer.Write(ParamStr(i) + ' ');
+
+ Writer.Write(' end.');
+ finally
+ Writer.DisposeOf;
+ end;
+
+ Exec(
+ TPath.Combine(
+ GetReg(HKEY_CURRENT_USER, 'Software\Embarcadero\BDS\20.0', 'RootDir'),
+ 'bin\dcc32.exe -Q -CC '
+ ) + DprPath,
+ False);
+
+ Exec(ExePath, True);
+ finally
+ if TFile.Exists(DprPath) then
+ TFile.Delete(DprPath);
+
+ if TFile.Exists(ExePath) then
+ TFile.Delete(ExePath);
+ end;
+end.
+```
+
+やっていることは単純で、
+
+1. コマンドライン引数をテンポラリファイルに保存する。その際 begin end. で挟む
+2. レジストリから Delphi 10.3 Rio の [Win32 コンパイラ dcc32.exe](http://docwiki.embarcadero.com/RADStudio/Rio/ja/DCC32.EXE_-_Delphi_%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%83%A9%E3%82%A4%E3%83%B3_%E3%82%B3%E3%83%B3%E3%83%91%E3%82%A4%E3%83%A9) のパスを取得する
+3. dcc32 に 1 で保存したファイルを渡す
+4. できあがった exe を実行する
+5. テンポラリファイルと exe ファイルを削除する
+
+と、コレだけです。
+
+ちなみに dcc32 に渡している引数の -Q はサイレントコンパイル(不要な情報が出力されなくなる), -CC はコンパイル対象がコンソール向け、という事を示します。
+-CC を指定しないと GUI ターゲットとなり起動に失敗します。
+
+実行するとこんな感じです。
+![image.png](https://qiita-image-store.s3.amazonaws.com/0/12977/8442b77a-0312-3de1-aee4-ab9ccbf0bf72.png)
+
+# ワンライナー FizzBuzz
+
+と dcce なんて作っていたせいで当初予定していたワンライン BrainF*ck が完成しませんでした…
+
+ということで、ワンライン FizzBuzz でお茶を濁すことに…
+それがコレです。
+
+```pascal
+for var i := 1 to 100 do Writeln((function(const M: array of String): String begin Result := M[Ord(i mod 3 = 0) or Ord(i mod 5 = 0) * 2] end)([(function(N: Integer): String begin Str(N, Result) end)(i), 'Fizz','Buzz','FizzBuzz']))
+```
+
+上のコードを読みやすくしたものがコレです。
+
+```pascal
+for var i := 1 to 100 do
+ Writeln(
+ (
+ function(const S: array of String): String
+ begin
+ Result := S[Ord(i mod 3 = 0) or Ord(i mod 5 = 0) * 2]
+ end
+ )([
+ (
+ function(N: Integer): String
+ begin
+ Str(N, Result)
+ end
+ )(i),
+ 'Fizz',
+ 'Buzz',
+ 'FizzBuzz'
+ ])
+ )
+```
+
+Object Pascal でのワンライナーは基本的に[セミコロンレス](https://qiita.com/pik/items/f2770a0591ef1196663b)と同じかなと思っています。
+ということで、無名関数をガッツリ使っています。
+もちろんセミコロンを使えないので uses も無し。System ユニットの関数だけで作ります。
+[Str 手続き](http://docwiki.embarcadero.com/Libraries/Rio/ja/System.Str)とか久しぶりに使った。
+
+ただ Delphi 10.3 Rio からは Pascal 伝統の var ブロック無しで[インライン変数宣言と型推論が使えるようになった](https://qiita.com/pik/items/ba2d26a05df6b0d555e5#%E8%A8%80%E8%AA%9E%E3%81%AE%E5%A4%89%E6%9B%B4%E7%AE%87%E6%89%80)ため完全なセミコロンレスが実現できています。
+
+実行した結果
+![image.png](https://qiita-image-store.s3.amazonaws.com/0/12977/c6dde033-9150-6372-b9d7-8ce1c77bfc8b.png)
+
+
+# まとめ
+勢いで Advent Calendar 作っちゃダメ!
+
+