Формат Golf String () обратный

13

Инвертировать метод Формат.

FormatМетод класса String (или эквивалент, такие как sprintf) доступен в большинстве языков. В основном он принимает строку «Формат», которая может содержать заполнители с некоторым дополнительным форматированием и ноль или более значений для вставки вместо этих заполнителей.

Ваша задача - реализовать обратную функцию на выбранном вами языке.

API

Имя метода должно быть либо format1или deformat.

Вход : 1-й параметр будет строкой «Формат», как и в оригинальном методе форматирования. Вторым параметром будет проанализированная строка (см. Примеры ниже). Никаких других параметров не требуется и не допускается.

Вывод : массив (или эквивалент вашего языка выбора) значений, которые были извлечены соответственно с заполнителями в формате.

Заполнители являются {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
@xnor - чем мы имеем неоднозначность, и каждое из перечисленного может быть действительным выход: ['', 'AAAA'], ['A', 'AA'],['AA', '']
Jacob
Можно ли тогда вывести deformat('{0}{1}{0}', 'ABBA') => ['', 'ABBA']? Если так, то есть дешевое решение, если каждая строка не появится хотя бы дважды.
xnor
будет ли работать ваше дешевое решение deformat('{0}_{1}_{0}', 'A_BB_A')?
Джейкоб
О, я вижу, я забыл о реальных персонажах в результатах. Я все еще пытаюсь понять, насколько это сложно алгоритмически. Дайте мне посмотреть, смогу ли я придумать действительно порочный пример.
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}) - не обеспечивает их равенство, отбрасывая соответствия для всех, кроме одного представления.

19 символов можно сохранить, опуская, mapKeys((0+).read)$если правильное упорядочение совпадений свыше 10 шаблонов не имеет значения, или может потребоваться заполнение до той же длины, или если допустимо упорядочение строк в шаблонах. В любом случае, если шаблон опущен в первом аргументе, он также опущен в результате.

Удаление !!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
Джон дворак
источник
где есть (0+)? не просто писать читать короче?
гордый haskeller
@proudhaskeller просто readоставляет вас с неоднозначным типом. Хаскель не знает, какой тип заказа для чтения ключей. +0Формирует число, из которого Haskell уже может сделать произвольный выбор и использует целые числа.
Джон Дворжак
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

5 символов могут быть сохранены путем предпочтения совпадений нулевой длины, принятия ABBAрешения ['', 'ABBA'], а не предпочтительного решения вопроса. Я решил интерпретировать примеры как подразумеваемую часть спецификации.

Натан Баум
источник
1

Питон, 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 }".

  • Это работает только для первых 10 заполнителей, поскольку предполагается, что они все 3 символа длиной.

  • Это не обрабатывает неоднозначные случаи вообще :(

Сэм Хаббард
источник
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

Шаблоны представлены в десятичном представлении, входные данные больше, чем 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;
}
Овидиу Андониу
источник