Formato de cadena de golf () inverso

13

Invierta el método de formato.

El Formatmétodo de la clase String (o equivalente, comosprintf ) está disponible en la mayoría de los idiomas. Básicamente toma una cadena de "Formato" que puede contener marcadores de posición con algún formato adicional y cero o más valores para insertar en lugar de esos marcadores de posición.

Su tarea es implementar la función inversa en el idioma que elija.

API

El nombre del método debe ser format1o deformat.

Entrada : el primer parámetro será la cadena de "Formato", al igual que en el método de formato original. El segundo parámetro será la cadena analizada (ver ejemplos a continuación). No se necesitan ni se permiten otros parámetros.

Salida : una matriz (o el equivalente de su idioma de elección) de valores que se extrajeron correspondientemente con los marcadores de posición en el formato.

Los marcadores de posición son {0}, {1}, {2}, etc.

En caso de mal formato, puede arrojar un error o devolver lo que quiera.

En caso de una entrada no válida, puede lanzar un error o devolver lo que desee. Entrada no válida es tal que no puede ser generada por el uso de String.Format misma cadena de formato, por ejemplo: '{0}{0}', 'AAB'.

Ejemplos

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

Ambigüedad

En caso de ambigüedad, puede devolver cualquier respuesta adecuada. Por ejemplo:

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

Algunas reglas más

  • Para hacerlo más fácil, no es necesario admitir el formateo. Puede olvidarse de los ceros a la izquierda, el punto decimal o los problemas de redondeo. Simplemente genere los valores como cadenas.
  • Para que no sea trivial, no se permiten expresiones regulares .
  • No necesita ocuparse de las llaves en la entrada (es decir, el segundo parámetro de entrada no contendrá ninguna {s o }s).

Victorioso

Este es el ! (debe leerse como "¡Esto es Esparta!") gana la función correcta que tenga la menor longitud. Las lagunas estándar están prohibidas.

Jacob
fuente
En el ejemplo deformat('{0}{1}{0}', 'ABBA') => ['A', 'BB'], ¿qué pasaría si nos dieran en cambio deformat('{0}{1}{0}', 'AAAA')?
xnor
@xnor - que tenemos una ambigüedad, y cada uno de los siguientes sería una salida válida: ['', 'AAAA'], ['A', 'AA'],['AA', '']
Jacob
¿Podría uno haber salido deformat('{0}{1}{0}', 'ABBA') => ['', 'ABBA']? Si es así, hay una solución barata a menos que cada cadena aparezca al menos dos veces.
xnor
¿Su solución barata también funcionará deformat('{0}_{1}_{0}', 'A_BB_A')?
Jacob
Ah, ya veo, me había olvidado de los personajes reales en los resultados. Todavía estoy tratando de entender lo algorítmicamente difícil que es esto. Déjame ver si puedo inventar una instancia realmente perversa.
xnor

Respuestas:

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

Se rompe si usa múltiples representaciones para el mismo patrón ( {1}vs {01}): no impone su igualdad, descartando coincidencias para todas menos una representación.

Se pueden guardar 19 caracteres omitiendo mapKeys((0+).read)$ si no importa el orden adecuado de las coincidencias por encima de 10 patrones, o si se puede requerir un relleno de la misma longitud, o si el orden de las cadenas de patrones es aceptable. En cualquier caso, si se omite un patrón del primer argumento, también se omite del resultado.

Eliminar !!0del final hace que se format1devuelva la lista de todas las soluciones, en lugar de solo la primera.

antes de jugar al golf:

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
fuente
¿Qué hay (0+)? no es solo leer leer más corto?
orgulloso Haskeller
@proudhaskeller solo readte deja con un tipo ambiguo. Haskell no sabe de qué tipo ordenable leer las claves. +0fuerza un número, del cual Haskell ya puede hacer una elección arbitraria y elige números enteros.
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

Se podrían guardar 5 caracteres prefiriendo coincidencias de longitud cero, haciendo la ABBAsolución ['', 'ABBA'], en lugar de la solución preferida de la pregunta. Elegí interpretar los ejemplos como una parte implícita de la especificación.

Nathan Baum
fuente
1

Python, 208 caracteres, aunque 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

La función barre ambas cadenas simultáneamente, hasta que encuentra una llave de apertura en la cadena de entrada, lo que significa un marcador de posición.

Luego, asume que el marcador de posición ya se ha expandido e intenta avanzar el índice de la cadena de salida más allá al mirar en la lista de valores encontrados hasta ahora.

Si no se ha expandido, agrega una nueva entrada a la lista de valores y comienza a agregar caracteres de la cadena de salida hasta que alcanza el carácter después del marcador de posición en la cadena de entrada.

Cuando llega al final de la cadena de entrada, devuelve los valores encontrados hasta ahora.


Funciona bien para entradas simples, pero tiene varios problemas:

  • Requiere un delimitador conocido después de cada marcador de posición en la entrada, por lo que no funciona con marcadores de posición uno al lado del otro, es decir, "{0} {1}". Es por eso que necesitaba agregar un carácter espacial a ambas cadenas.

  • Se supone que las primeras instancias de cada marcador de posición están en orden, por ejemplo, "{ 0 } { 1 } {1} {0} { 2 }".

  • Solo funciona para los primeros 10 marcadores de posición, ya que supone que tienen 3 caracteres de longitud.

  • No maneja casos ambiguos en absoluto :(

Sam Hubbard
fuente
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;}

La función format1 tiene 2 cadenas como entrada (const char *) y devuelve un hashmap con claves enteras (el patrón) y el valor es la cadena identificada. Si no se encuentra nada o algún error, se devuelve un hashmap vacío.

Uso:

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

Salida:

1=one
2=two

Ejemplo 2

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

Salida:

one and two

Los patrones están en representación decimal, las entradas más grandes que MAXINTse desbordarán pero aún funciona.

Aunque hay soluciones más pequeñas en otros lenguajes de programación, este es el C ++ más pequeño, ¡todavía! :)

Este es el código antes de jugar al golf:

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