Формат гольф-струни () зворотний


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.

Ще кілька правил

  • Щоб зробити це легше, фактично не потрібно підтримувати форматування. Ви можете забути про основні нулі, питання про десяткові крапки або округлення. Просто генеруйте значення у вигляді рядків.
  • Щоб зробити його нетривіальним, регулярні вирази заборонені .
  • Вам не потрібно дбати про фігурні дужки введення (тобто другий вхідний параметр не буде містити {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

whi є (0+)? не просто писати читати коротше?
гордий haskeller

@proudhaskeller просто readзалишає вас неоднозначним типом. Haskell не знає, який упорядкований тип читати як ключі. +0змушує число, з якого Хаскелл вже в змозі зробити довільний вибір і йде на цілі числа.
Джон Дворак

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 заповнювачів, оскільки передбачає, що вони всі три символи.

  • Він взагалі не обробляє неоднозначні випадки :(


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;
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.