1. pik

    No comment

    pik
Changes in body
Source | HTML | Preview
@@ -1,190 +1,190 @@
# 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 ターゲットとなり起動に失敗します。
実行するとこんな感じです。
-さすが Delphi !まるでインタプリタみたいな速度で動く!
+さすが Delphi !**まるでインタプリタみたいな速度で動く!**
![dcce.gif](https://qiita-image-store.s3.amazonaws.com/0/12977/63a06c66-5b67-29b7-342c-eadcf730b910.gif)
# ワンライナー 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)
![fizzbuzz.gif](https://qiita-image-store.s3.amazonaws.com/0/12977/552a69b8-7147-3bec-60ed-a6df8f83086c.gif)
# まとめ
勢いで Advent Calendar 作っちゃダメ!
# おまけ
もしも、この記事で Delphi が気になった方は、商用版と同じ機能が使える完全無料の [Delphi 10.3 Rio Community Edition](https://www.embarcadero.com/jp/products/delphi/starter/free-download) を使ってみて下さい!
無料で Windows, macOS, Android, iOS の開発ができるよ!