Golf Stringのformat()逆


13

Formatメソッドを反転します。

FormatStringクラスのメソッド(またはのような同等のメソッドsprintf)は、ほとんどの言語で使用できます。基本的には、いくつかの追加の書式設定を持つプレースホルダーと、それらのプレースホルダーの代わりに0個以上の値が挿入される「Format」文字列を取ります。

あなたの仕事は、選択した言語で逆関数を実装することです。

API

メソッド名のいずれかでなければなりませんformat1deformat

入力:1番目のパラメーターは、元のformatメソッドと同様に、「Format」文字列になります。2番目のパラメーターは、解析された文字列になります(以下の例を参照)。他のパラメーターは不要であり、許可されていません。

出力:形式のプレースホルダーに対応して抽出された値の配列(または選択した言語に相当するもの)。

プレースホルダーです{0}{1}{2}、など

不正な形式の場合、エラーをスローしたり、好きなものを返したりすることがあります。

無効な入力の場合、エラーをスローするか、好きなものを返します。無効な入力は、同じフォーマット文字列を使用してString.Formatで生成できないようなものです'{0}{0}', 'AAB'。例:。

deformat('{0} {1}', 'hello world') => ['hello', 'world']
deformat('http{0}://', 'https://') => ['s']
deformat('http{0}://', 'http://') => [''] // array of one item which is an empty string
deformat('{0}{1}{0}', 'ABBA') => ['A', 'BB']

あいまいさ

あいまいな場合は、適切な回答を返すことができます。例えば:

deformat('{0} {1}', 'Edsger W. Dijkstra')
// both ['Edsger', 'W. Dijkstra'] and ['Edsger W.', 'Dijkstra'] are applicable.

いくつかのルール

  • 簡単にするために、実際にフォーマットをサポートする必要はありません。先行ゼロ、小数点、または丸めの問題をすべて忘れることができます。値を文字列として生成するだけです。
  • それを非自明にするために、正規表現は許可されていません
  • 入力中の中括弧に注意する必要はありません(つまり、2番目の入力パラメーターには{sまたは}s は含まれません)。

勝ち

これはです!(「これはスパルタです!」と読む必要があります)最短の長さを持つ正しい関数が勝ちます。標準的な抜け穴は禁止されています。


例ではdeformat('{0}{1}{0}', 'ABBA') => ['A', 'BB']、代わりに与えられた場合はどうなりdeformat('{0}{1}{0}', 'AAAA')ますか?
xnor 14

@xnor -私たちは、曖昧さを持っているし、次のそれぞれが有効な出力であるより:['', 'AAAA']['A', 'AA']['AA', '']
ヤコブ

次に出力できましたdeformat('{0}{1}{0}', 'ABBA') => ['', 'ABBA']か?その場合、すべての文字列が少なくとも2回表示されない限り、安価なソリューションがあります。
xnor 14

あなたの安価なソリューションもうまくいきますdeformat('{0}_{1}_{0}', 'A_BB_A')か?
ジェイコブ14

ああ、なるほど、結果の実際のキャラクターを忘れていました。私は今でもこれがどれほどアルゴリズム的に難しいかについて頭を悩ませようとしています。本当にひねくれたインスタンスを作成できるかどうかを確認します。
XNOR

回答:


2

Haskell、220文字

import Data.Map;f""""=[empty]
f('{':b)d=[insert k m b|(k,('}':a))<-lex b,(m,c)<-[splitAt n d|n<-[0..length d]],b<-f a c,notMember k b||b!k==m]
f(x:b)(y:d)|x==y=f b d;f _ _=[];format1 x y=elems$mapKeys((0+).read)$f x y!!0

同じパターンに複数の表現を使用すると中断します({1}vs {01})-同等性を強制せず、代わりに1つを除くすべての表現の一致を破棄します。

mapKeys((0+).read)$10パターンを超える一致の適切な順序が重要でない場合、同じ長さのパディングが必要な場合、またはパターンの文字列順序が許容される場合は、省略して19文字を保存できます。いずれの場合でも、パターンが最初の引数から省略されると、結果からも省略されます。

!!0最後から削除するformat1と、最初のソリューションではなく、すべてのソリューションのリストが返されます。

ゴルフ前:

import Data.Map
import Control.Monad

cuts :: [a] -> [([a],[a])]
cuts a=[splitAt n a | n <- [0..length a]]

f :: String -> String -> [Map String String]
-- empty format + empty parsed = one interpretation with no binding
f "" "" = [empty]
-- template-start format + some matched = branch search
f ('{':xs) ys = do
    let [(key, '}':xr)] = lex xs
    (match, yr) <- cuts ys
    b <- f xr yr
    guard $ notMember key b || b!key == match
    return $ insert key match b
-- non-empty format + matching parsed = exact match
f (x:xs) (y:ys) | x == y = f xs ys
-- anything else = no interpretation
f _ _ = []

deformat :: String -> String -> [String]
deformat x y = elems $ mapKeys ((0+).read) $ head $ f x y

whiはありますか(0+)?単に読むだけで短くなるのではありませんか?
誇りに思ってhaskeller 14

@proudhaskeller readは、あいまいなタイプを残します。Haskellは、キーを読み取る順序可能な型を知りません。+0Haskellが既に任意の選択を行うことができ、整数を求める番号を強制します。
ジョンドヴォルザーク14

2

ルビー、312文字

class String
def-@
self[0,1].tap{self[0,1]=''}end
end
def format1 f,s,r=[]
loop{if'{'==c=-f
n,f=f.split('}',2)
[*1..s.length,0].each{|i|next if'{'!=f[0]&&s[i]!=f[0]
if u=format1((g=f.gsub("{#{n}}",q=s[0,i])).dup,s[i..-1],r.dup)
r,s,f=u,s[i..-1],g
r[n.to_i]=q
break
end}else
c!=-s&&return
end
""==c&&break}
r
end

質問の優先解決ABBA['', 'ABBA']ではなく、長さゼロの一致を優先して解決策を作成すると、5文字を節約できます。例は仕様の暗黙の一部として解釈することにしました。


1

Python、208文字、不完全ではありますが。

def format1(i,o):
 i+=" ";o+=" ";x=y=0;s=[]
 while x<len(i):
  if i[x]=="{":
   try:y+=len(s[int(i[x+1])])
   except:
    s+=[""]
    while o[y]!=i[x+3]:s[int(i[x+1])]+=o[y];y+=1
   x+=3
  x+=1;y+=1
 return s

この関数は、プレースホルダーを示す入力文字列の左中括弧を見つけるまで、両方の文字列を同時にスイープします。

次に、プレースホルダーが既に展開されていると想定し、これまでに見つかった値のリストを参照して、出力文字列のインデックスを進めようとします。

展開されていない場合は、値のリストに新しいエントリを追加し、入力文字列のプレースホルダーの後の文字に達するまで、出力文字列から文字を追加し始めます。

入力文字列の最後に到達すると、それまでに見つかった値を返します。


単純な入力ではうまく機能しますが、いくつかの問題があります。

  • 入力の各プレースホルダーの後に既知の区切り文字が必要なので、「{0} {1}」のように互いに隣り合うプレースホルダーでは機能しません。これが、両方の文字列にスペース文字を追加する必要がある理由です。

  • 各プレースホルダーの最初のインスタンスは、例えば「{ 0 } { 1 } {1} {0} { 2 }」の順序であると想定しています。

  • すべて3文字の長さであると想定されるため、最初の10個のプレースホルダーでのみ機能します。

  • あいまいなケースはまったく処理しません:(


1

C ++ 11コード、386文字

#include <string>
#include <map>
using namespace std;using _=map<int,string>;using X=const char;_ format1(X*p,X*s,_ k=_()){_ r;while(*p!='{'){if(!*p||!*s){return*p==*s?k:r;}if(*p++!=*s++)return r;}int v=0;while(*++p!='}'){v=v*10+(*p-48);}p++;if(k.find(v)!=k.end()){return format1((k[v]+p).c_str(),s,k);}while((r=format1(p,s,k)).empty()){k[v]+=*s++;if(!*s){return*p==*s?k:r;}}return r;}

format1関数は、入力として2つの文字列(const char *)を持ち、キー整数(パターン)を持つハッシュマップを返し、値は識別された文字列です。何も見つからないか、エラーがある場合、空のハッシュマップが返されます。

使用法:

for (auto v : format1("{1} {2}", "one two")){
    cout << v.first << "=" << v.second << endl;
}

出力:

1=one
2=two

例2:

auto v = format1("{1} {2}", "one two");
cout << v[1] << " and " << v[2] << endl;

出力:

one and two

パターンは10進表記で、入力MAXINTはオーバーフローよりも大きくなりますが、それでも機能します。

他のプログラミング言語には小さなソリューションがありますが、これは最小のC ++です。:)

これはゴルフをする前のコードです:

#include <string>
#include <map>
using namespace std;

using res = map<int,string>;

res format1(const char* p, const char* s, res k=res()){
    res r; // intermediate result, empty until the end
    // match until first '{'
    while (*p != '{'){
        if (!*p || !*s){
            // exit case
            return ((*p == *s) ? k : r); // == 0
        }
        if (*p++ != *s++)
               return r;
    }

    // *p == '{'
    int v = 0;
    while(*++p != '}'){
        v = v*10 + (*p - '0');
    }
    p++; // advance past '}'

    // match back-references
    if (k.find(v) != k.end()){
       return format1((k[v]+p).c_str(), s, k);
    }

    // recursive search
    while ( (r=format1(p, s, k)).empty() ){
        k[v] += *s++;
        if (!*s){
            return *p == *s ? k : r;
        }
    }
    return r;
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.