Inverso do formato Golf String ()

13

Inverta o método Format.

O Formatmétodo da classe String (ou equivalente, como sprintf) está disponível na maioria dos idiomas. Basicamente, é necessária uma string "Format" que pode conter espaços reservados com alguma formatação extra e zero ou mais valores a serem inseridos em vez desses espaços reservados.

Sua tarefa é implementar a função inversa no idioma de sua escolha.

API

O nome do método deve ser format1ou deformat.

Entrada : o 1º parâmetro será a string "Format", assim como no método de formato original. O segundo parâmetro será a string analisada (veja os exemplos abaixo). Nenhum outro parâmetro é necessário nem permitido.

Saída : uma matriz (ou o equivalente do idioma da sua escolha) de valores que foram extraídos correspondentemente com os espaços reservados no formato.

Os marcadores são {0}, {1}, {2}, etc.

Em caso de formato incorreto, você pode gerar um erro ou retornar o que quiser.

Em caso de entrada inválida, você pode gerar um erro ou retornar o que quiser. Dados de entrada é de tal forma que não pode ser gerado por String utilizando a mesma cadeia de formato, por exemplo: '{0}{0}', 'AAB'.

Exemplos

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

Ambiguidade

Em caso de ambiguidade, você pode retornar qualquer resposta adequada. Por exemplo:

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

Mais algumas regras

  • Para facilitar, não há necessidade de oferecer suporte à formatação. Você pode esquecer tudo sobre zeros à esquerda, ponto decimal ou questões de arredondamento. Apenas gere os valores como strings.
  • Para torná-lo não trivial, expressões regulares não são permitidas .
  • Você não precisa cuidar de chaves na entrada (ou seja, 2 parâmetro de entrada não conterá quaisquer {s ou }s).

Ganhando

Isso é ! (deve ser lido como "This is Sparta!"): a função correta com o menor comprimento vence. As brechas padrão são proibidas.

Jacob
fonte
No exemplo deformat('{0}{1}{0}', 'ABBA') => ['A', 'BB'], e se tivéssemos recebido deformat('{0}{1}{0}', 'AAAA')?
Xnor
@xnor - que temos uma ambigüidade, e cada um dos seguintes seria uma saída válida: ['', 'AAAA'], ['A', 'AA'],['AA', '']
Jacob
Alguém poderia então ter produzido deformat('{0}{1}{0}', 'ABBA') => ['', 'ABBA']? Nesse caso, existe uma solução barata, a menos que cada string apareça pelo menos duas vezes.
xnor
sua solução barata também funcionará deformat('{0}_{1}_{0}', 'A_BB_A')?
Jacob
Ah, entendi, eu tinha esquecido os personagens reais nos resultados. Eu ainda estou tentando entender como isso é algoritmicamente difícil. Deixe-me ver se consigo criar uma instância realmente perversa.
Xnor

Respostas:

2

Haskell, 220 caracteres

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

Quebras se você usar várias representações para o mesmo padrão ( {1}vs {01}) - não impõe sua igualdade, descartando correspondências para todas, exceto uma representação.

Podem ser salvos 19 caracteres omitindo mapKeys((0+).read)$se a ordenação adequada de correspondências acima de 10 padrões não importa, ou se for necessário o preenchimento com o mesmo comprimento, ou se a ordenação de sequência de padrões é aceitável. De qualquer forma, se um padrão for omitido no primeiro argumento, ele também será omitido no resultado.

Remover !!0do final format1devolve a lista de todas as soluções, e não apenas a primeira.

antes de jogar golfe:

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
fonte
o que há (0+)? não é apenas escrever ler mais curto?
haskeller orgulhoso
O @proudhaskeller apenas readdeixa você com um tipo ambíguo. Haskell não sabe que tipo ordenável deve ler as chaves. +0força um número, do qual Haskell já é capaz de fazer uma escolha arbitrária e usa números inteiros.
John Dvorak
2

Ruby, 312 caracteres

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

É possível salvar cinco caracteres preferindo correspondências de comprimento zero, criando a ABBAsolução ['', 'ABBA'], em vez da solução preferida da pergunta. Eu escolhi interpretar os exemplos como uma parte implícita da especificação.

Nathan Baum
fonte
1

Python, 208 caracteres, embora incompleto.

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

A função varre as duas seqüências simultaneamente, até encontrar uma chave de abertura na sequência de entrada, significando um espaço reservado.

Em seguida, ele assume que o espaço reservado já foi expandido e tenta avançar o índice da sequência de saída além dele, procurando na lista de valores encontrados até agora.

Se não tiver sido expandido, ele adiciona uma nova entrada à lista de valores e começa a adicionar caracteres da sequência de saída até atingir o caractere após o espaço reservado na sequência de entrada.

Quando chega ao final da string de entrada, ele retorna os valores encontrados até o momento.


Funciona bem para entradas simples, mas possui vários problemas:

  • Ele requer um delimitador conhecido após cada espaço reservado na entrada, para que não funcione com espaços reservados próximos um do outro, por exemplo, "{0} {1}". É por isso que eu precisei acrescentar um caractere de espaço às duas strings.

  • Ele assume que as primeiras instâncias de cada espaço reservado estão em ordem, por exemplo, "{ 0 } { 1 } {1} {0} { 2 }".

  • Ele funciona apenas para os 10 primeiros marcadores de posição, pois pressupõe que todos os três caracteres sejam longos.

  • Ele não lida com casos ambíguos :(

Sam Hubbard
fonte
1

Código C ++ 11, 386 caracteres

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

A função format1 possui 2 strings como entrada (const char *) e retorna um hashmap com as teclas número inteiro (o padrão) e value é a string identificada. Se nada for encontrado ou algum erro, um hashmap vazio será retornado.

Uso:

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

Resultado:

1=one
2=two

Exemplo 2:

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

Resultado:

one and two

Os padrões estão em representação decimal, entradas maiores do que MAXINTexcederão, mas ainda funcionam.

Embora existam soluções menores em outras linguagens de programação, este é o menor C ++ - ainda! :)

Este é o código antes de jogar golfe:

#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
fonte