Odwrotny format Golf String ()

13

Odwróć metodę formatowania.

FormatMetoda klasy String (lub equivallent, takich jak sprintf) jest dostępny w większości języków. Zasadniczo wymaga ciągu „Format”, który może zawierać symbole zastępcze z dodatkowym formatowaniem oraz zero lub więcej wartości do wstawienia zamiast tych symboli zastępczych.

Twoim zadaniem jest zaimplementowanie funkcji odwrotnej w wybranym języku.

API

Nazwa metody powinny być format1albo deformat.

Dane wejściowe : 1. parametrem będzie ciąg „Format”, podobnie jak w oryginalnej metodzie formatowania. Drugi parametr będzie analizowanym ciągiem (patrz przykłady poniżej). Żadne inne parametry nie są potrzebne ani dozwolone.

Dane wyjściowe : tablica (lub odpowiednik wybranego języka) wartości, które zostały wyodrębnione odpowiednio z symbolami zastępczymi w formacie.

Symbole zastępcze są {0}, {1}, {2}, etc.

W przypadku złego formatu możesz zgłosić błąd lub zwrócić cokolwiek zechcesz.

W przypadku nieprawidłowych danych wejściowych możesz zgłosić błąd lub zwrócić cokolwiek zechcesz. Nieprawidłowy wejście jest taka, że nie mogą być generowane przez String.Format przy użyciu tego samego formatu znaków, na przykład: '{0}{0}', 'AAB'.

Przykłady

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']

Dwuznaczność

W przypadku niejasności możesz zwrócić dowolną odpowiednią odpowiedź. Na przykład:

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

Więcej zasad

  • Aby to ułatwić, nie ma potrzeby obsługi formatowania. Możesz zapomnieć o wiodących zerach, kropkach dziesiętnych lub problemach z zaokrąglaniem. Po prostu wygeneruj wartości jako ciągi.
  • Aby uczynić to nietrywialnym, Wyrażenia regularne są niedozwolone .
  • Nie trzeba dbać o klamrami na wejściu (czyli parametr 2-ty wejście nie będzie zawierać żadnych {s lub }s).

Zwycięski

To jest ! (należy czytać jako „To jest Sparta!”) wygrywa poprawna funkcja o najkrótszej długości. Standardowe luki są zabronione.

Jakub
źródło
W tym przykładzie deformat('{0}{1}{0}', 'ABBA') => ['A', 'BB'], co jeśli otrzymalibyśmy zamiast tego deformat('{0}{1}{0}', 'AAAA')?
xnor
@xnor - niż mamy niejednoznaczność, a każdy z poniższych byłby ważny wyjście: ['', 'AAAA'], ['A', 'AA'],['AA', '']
Jacob
Czy można było wtedy wyprowadzić deformat('{0}{1}{0}', 'ABBA') => ['', 'ABBA']? Jeśli tak, istnieje tanie rozwiązanie, chyba że każdy ciąg pojawi się co najmniej dwa razy.
xnor
czy twoje tanie rozwiązanie również będzie działać deformat('{0}_{1}_{0}', 'A_BB_A')?
Jacob
Och, rozumiem, zapomniałem o rzeczywistych postaciach w wynikach. Wciąż próbuję owinąć głowę, jak to algorytmicznie trudne. Zobaczmy, czy mogę stworzyć naprawdę przewrotną instancję.
xnor

Odpowiedzi:

2

Haskell, 220 znaków

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

Łamie się, jeśli używasz wielu reprezentacji dla tego samego wzorca ( {1}vs {01}) - nie wymusza ich równości, odrzucając dopasowania dla wszystkich reprezentacji oprócz jednej.

Można zapisać 19 znaków, pomijając, mapKeys((0+).read)$jeśli prawidłowe uporządkowanie dopasowań powyżej 10 wzorców nie ma znaczenia, lub może być wymagane wypełnienie do tej samej długości, lub jeśli dopuszczalne jest uporządkowanie wzorców łańcuchów. W każdym razie, jeśli wzorzec zostanie pominięty w pierwszym argumencie, zostanie również pominięty w wyniku.

Usunięcie !!0z końca powoduje format1zwrócenie listy wszystkich rozwiązań, a nie tylko pierwszego.

przed golfem:

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
John Dvorak
źródło
kto tam jest (0+)? czy pisanie nie jest krótsze?
dumny haskeller,
@proudhaskeller po prostu readpozostawia niejednoznaczny typ. Haskell nie wie, jaki typ zamówienia ma odczytać klucze. +0wymusza liczbę, z której Haskell jest już w stanie dokonać arbitralnego wyboru i wybiera liczby całkowite.
John Dvorak,
2

Ruby, 312 znaków

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

Można zapisać 5 znaków, preferując dopasowania o zerowej długości, co stanowi ABBArozwiązanie ['', 'ABBA']zamiast preferowanego rozwiązania pytania. Wybrałem interpretację przykładów jako dorozumianą część specyfikacji.

Nathan Baum
źródło
1

Python, 208 znaków, choć niekompletny.

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

Funkcja zamiata oba ciągi jednocześnie, aż znajdzie nawias otwierający w ciągu wejściowym, co oznacza symbol zastępczy.

Następnie zakłada, że ​​symbol zastępczy został już rozwinięty, i próbuje przesunąć indeks ciągu wyjściowego obok niego, przeglądając listę znalezionych do tej pory wartości.

Jeśli nie został rozwinięty, dodaje nowy wpis do listy wartości i rozpoczyna dodawanie znaków z ciągu wyjściowego, dopóki nie osiągnie znaku po symbolu zastępczym w ciągu wejściowym.

Gdy dojdzie do końca ciągu wejściowego, zwraca znalezione do tej pory wartości.


Działa dobrze w przypadku prostych danych wejściowych, ale ma wiele problemów:

  • Wymaga znanego separatora po każdym symbolu zastępczym w danych wejściowych, więc nie działa z symbolami zastępczymi obok siebie, tj. „{0} {1}”. Dlatego musiałem dodać znak spacji do obu łańcuchów.

  • Zakłada, że ​​pierwsze wystąpienia każdego symbolu zastępczego są w porządku, np. „{ 0 } { 1 } {1} {0} { 2 }”.

  • Działa tylko dla pierwszych 10 symboli zastępczych, ponieważ zakłada, że ​​wszystkie mają 3 znaki.

  • W ogóle nie obsługuje niejednoznacznych przypadków :(

Sam Hubbard
źródło
1

Kod C ++ 11, 386 znaków

#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;}

Funkcja format1 ma 2 ciągi wejściowe (const char *) i zwraca skrót z kluczami całkowitymi (wzorzec), a wartością jest zidentyfikowany ciąg. Jeśli nic nie zostanie znalezione lub wystąpi błąd, zwracana jest pusta mapa.

Stosowanie:

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

Wynik:

1=one
2=two

Przykład 2:

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

Wynik:

one and two

Wzory są w postaci dziesiętnej, dane wejściowe większe niż MAXINTzostaną przepełnione, ale nadal działa.

Mimo że istnieją inne rozwiązania w innych językach programowania, jest to najmniejszy C ++ - jak dotąd! :)

Oto kod przed golfem:

#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;
}
Ovidiu Andoniu
źródło