ログラインのインターリーブ解除:ハードモード
オリジナルを栄光のソートタスクに変えたいくつかの残念なルールに直面して、私はより挑戦的なバリアントを投稿しています。元の課題を改善する方法の提案については、ルイスメンドに叫んでください。
すべて同じログに出力する複数のアプリを実行するサーバーを継承しました。
あなたの仕事は、ソースごとにログファイルの行をインターリーブ解除することです。幸いなことに、すべてのアプリを作成した人は、ソースを示すタグを残すのに十分親切でした。
ログ
各行は次のようになります。
[app_name] Something horrible happened!
- アプリタグは常に角かっこで囲まれ、英数字とアンダースコアのみが含まれます。
- アプリのタグは空ではありません
- 後で任意の行に他の角括弧がある場合がありますが、有効なタグを形成するものはありません。
- タグの後には、常に少なくとも1つの非スペース文字があります。
- ログ全体が空の場合があります。
- ファイルに存在する一意のアプリタグの数に制限はありません。
場合によっては、アプリタグが欠落している可能性があります。この場合、ログ行は最後にログに記録されたアプリに属します。
- ログの最初の行は常にアプリタグで始まります
- で始まる行
[
は必ずしもタグ付けされていません。最初の角括弧の間に無効な文字があるか]
、またはない場合、その行はタグ付けされません。 - ログに空の行は表示されません
期待される出力
アプリタグが存在する各ログ行から削除された、完全に分離されたいくつかのログを出力する必要があります。ログ行の先頭の空白を保持する必要はありません。
出力ログは、ある種のKey-Valueマッピングまたは妥当な同等のものである必要があります。有効な出力形式の非網羅的なリスト:
- 各アプリのアプリタグにちなんで名付けられたファイル
- この場合、出力ファイルは出力ディレクトリにまだ存在していないと想定できます。
- アプリタグをキーとして使用し、改行で区切られたログ行の文字列を値として使用する辞書/マップ/ハッシュ/その他。
- 空白行で区切られ、アプリタグが前に付いた長い連結文字列
- [キー、値]リストのリスト
- アプリタグをキーとして、ログ行の配列を値として持つJSON文字列
- アプリタグをヘッダーとして使用し
#
、行の先頭を円記号でエスケープしたマークダウンドキュメント。 - 文字列を入力として受け取り、関連付けられたログを改行で区切られた文字列として出力するJavascript関数。
基本的に、ログ行がどのアプリからのものかわからない場合、出力は無効です。
例
ログ全体は次のようになります。
[weather] Current temp: 83F
[barkeep] Fish enters bar
Fish orders beer
[stockmarket] PI +3.14
[PI announced merger with E]
[barkeep] Fish leaves bar
[weather] 40% chance of rain detected
[ I have a lovely bunch of coconuts
3つの異なるログを出力する必要があります。
天気:
Current temp: 83F
40% chance of rain detected
[ I have a lovely bunch of coconuts
バーキープ:
Fish enters bar
Fish orders beer
Fish leaves bar
株式市場:
PI +3.14
[PI announced merger with E]
事前にアプリタグの名前が与えられることはありません。ログファイルを分析することによってのみそれらを決定する必要があります。
ルールとスコアリング
- これはコードゴルフなので、最短のコードが優先されます。
- 標準のルールと抜け穴が適用されます
- 各入力行が事前に解析されたタグ+メッセージではなく文字列として表される場合は、任意の便利なIO形式を使用してください。構文解析はこの課題の一部です。
- 各アプリの出力ログ行は、元のログと同じ順序で表示される必要があります。
- 入力ログにはASCII文字のみが含まれていると想定できます。
回答
Python 3.8、95バイト
import re
lambda x:[((t:=re.match(r'\[(\w*)\]',s)or t)[1],s.split(t[0])[-1].strip())for s in x]
オンラインでお試しください!
(入力付きの拡張TIOの例)
説明:
:=
オペレーターにはPython3.8が必要です。これは、文字列のリストを入力として受け取り、(tag, body)
タプルのリストを出力します。まず、正規表現の一致を使用してタグを取得します。
t:=re.match(r'\[(\w*)\]',s)or t)
これは、角かっこで囲まれた単語文字(英数字+アンダースコア)の最初のシーケンスと一致し、単語はキャプチャグループとして使用されます。文字列がこの正規表現に一致する場合、完全一致とグループの2つの要素をt
持つmatch
オブジェクトになります。文字列がある場合たとえば、[tag] body
、match
要素を持つことになります[tag]
とtag
。
文字列がこの正規表現と一致しない場合は、re.match()
Noneを返します。コードはt = None or t
、になります。これはちょうどですt = t
。したがって、タグは前の行の値を保持します。最初の行に一致がなかった場合、エラーが発生しますが、心配する必要はありません。
コードは、タプル構築t[1], s.split(t[0])[-1].strip()
、t[1]
キャプチャグループ(角括弧なしのタグ)であり、t[0]
タグであると角かっこ。タグ全体で文字列を分割すると、タグが実際に文字列に存在するかどうかに関係なく、本文が分離されます。
Retina 0.8.2、95バイト
+m`^(\[\w+] ).*¶(?!\[\w+])
$&$1
O$`(\w+).* $1
¶
¶¶
rm`(?<=^\1.*¶)¶(.\w+].)
(?<=(^|¶¶).\w+]).
¶
オンラインでお試しください!説明:
+m`^(\[\w+] ).*¶(?!\[\w+])
$&$1
タグなしのすべての行にタグを付けます。
O$`(\w+).* $1
元の課題に対する私の答えから取った行を並べ替えます。
¶
¶¶
行をダブルスペースにします。
rm`(?<=^\1.*¶)¶(.\w+].)
重複するタグとその前の空の行を削除します。つまり、残っている空の行は、個別のタグを区切る行だけです。
(?<=(^|¶¶).\w+]).
¶
タグを独自の行に移動します。
perlの-Mfeature =、-n言う
47の
46のバイトを
(@Dom Hastingsの好意で1バイト保存)
$;=$1 if s/^\[(\w+)\] +//;$;{$;}.=$_}{say for%;
オンラインでお試しください!
これはどのように作動しますか?
まず、-n
スイッチの効果。これにより、Perlはプログラムをループでラップし、入力を読み取り、各行の本文を実行します。ただし、これは非常に洗練されていない方法で行われ、次のように、解析を行う前に本体をループでラップします。
perl -ne 'TEXT'
になります
LINE: while (defined($_ = readline ARGV)) {
TEXT;
}
しかし、それはあなたTEXT
がフォームの場合、あなたはLOOP_BODY}{FINAL_STATEMENT
プログラムで終わることを意味します:
LINE: while (defined($_ = readline ARGV)) {
LOOP_BODY
}
{
FINAL_STATEMENT;
}
このトリックは、END
ブロック全体で数バイトを節約するためだけに使用しています。
プログラム自体では、簿記を行うために2つの変数を使用しています。$;
現在のタグが含まれ、ハッシュ%;
では、各タグの行を追跡します。ここで、入力の各行について、タグで始まるかどうかを確認し、始まる場合は行から削除して、タグを記憶します。
$; = $1 if # Remember the tag if,
s/^\[(\w+)\] +//; # we can strip of a tag
次に、現在の行(存在する場合はタグから削除)を、そのタグ用にすでに収集されている行のセットに連結します。そのような行がない場合は、空の文字列と効果的に連結します。
$;{$;}.=$_ # Remember the current line
最後に、すべての行を読み取った後、ハッシュを出力します。Perlは、ハッシュをリストとして扱い、キーと値を交互に使用する場合、ハッシュを単純なリストにフラット化するのに便利です。これにより、各セクションが改行で区切られ、タグが先頭にある出力が得られます。
say for%; # Print the flattened hash
05AB1E、22バイト
vyD']¡н¦DžjÃÊi‚»]).¡#н
行のリストとして入力し、複数行の文字列のリストのリストとして出力します。
オンラインでお試しください(きれいに印刷されています。実際の出力を確認するには、フッターを自由に削除してください)。
説明:
v # Loop `y` over each string of the (implicit) input-list:
yD # Push line `y` twice
']¡ '# Split the copy on "]"
н # Only leave the first part
¦ # Remove the leading character (the potential "[")
D # Duplicate it
žj # Push builtin string "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"
à # Only keep those characters in the string we duplicated
Êi # If it is NOT equal to the string:
‚ # Pair it with the previous line
» # And join that pair with a newline delimiter
] # Close both the if-statement and loop
) # Wrap all values on the stack into a list
.¡ # Group all strings by:
# # Split the string on spaces
н # And only leave the first part (the tag)
# (after which the result is output implicitly)
AWK-F]
、122の 123 113バイト
water_ghostsが親切に指摘したバグを修正するためのバイトを追加しました。
ジュゼッペのおかげで10バイト節約できました!!!
/^\[\w+\]/{a[l=$1][i++]=$2;next}{a[l][i++]=$0}END{for(k in a){print"\n",substr(k,2);for(j in a[k])print a[k][j]}}
オンラインでお試しください!
SimpleTemplate、142バイト
まあ、これはそれほど難しいことではありませんでした。
この回答は、次のわずかに変更されたバージョンです。ログ行のインターリーブ解除
{@callexplode intoL EOL,argv.0}{@eachL}{@if_ matches"@^(\[\w+\]) ?(.+)$@"M}{@setX"#{M.1} "}{@set_ M.2}{@/}{@setS.[X]S.[X],X,_,EOL}{@/}{@echoS}
ゴルフなし:
これはかなり読めないので、以下は読めるバージョンです:
{@call explode into lines EOL, argv.0}
{@set storage null}
{@each lines as line}
{@if line matches "@^(\[\w+\]) ?(.+)$@" match}
{@set last "#{match.1} "}
{@set line match.2}
{@/}
{@set storage.[last] storage.[last], last, line, EOL}
{@/}
{@echo storage}
変更点:
新しい要件で正しく機能するには、いくつかの変更を行う必要がありました。以下は、リンクされた回答のコピーです。
{@call explode into lines EOL, argv.0}
{@set storage null}
{@each lines as line}
{@if line matches "@^(\[.*\])@" match}
{@set storage.[match.1] storage.[match.1], line, EOL}
{@/}
{@/}
{@echo storage}
以下は、変更の完全なリストです。
- 正規表現は、スペースが存在する場合はスペースなしで、残りのコンテンツに一致するように変更されました。(引用:「タグの後には常に少なくとも1つの非スペース文字があります。」)
- 後で使用し、行を正規化するために、「アプリ」を追加のスペースで格納します(「タグ」の直後にスペースがある場合とない場合があります)。
- 最初のスペースを含まない残りのコンテンツを
line
(_
ゴルフバージョンの場合)変数に格納します - 以前は
line
変数の一部であったline
変数の前に「タグ」を追加します。
ご覧のとおり、変更はそれほど重要ではありません。コードを移動し、スペースを追加し、出力に変数を追加します。
あなたはこれを試すことができます: http://sandbox.onlinephpfunctions.com/code/eb5380ba1826530087fd92fa71d709c0b2d6de39
Scala、127バイト
l=>((("",List[(String,String)]())/:l){case((p,m),s"[$t] $b")=>(t,(t,b)::m)case((p,m),b)=>(p,(p,b)::m)})._2.groupMap(_._1)(_._2)
Scastieで試してみてください(TIOでは機能しません)
うわー、これは長いです。