Golf String的format()反


13

反转Format方法。

大多数语言都提供FormatString类的方法(或等价的方法,例如sprintf)。它基本上采用“格式”字符串,其中可能包含具有某些额外格式的占位符,并且要插入零个或多个值来代替这些占位符。

您的任务是以您选择的语言实现逆函数。

API

方法名称应为format1deformat

输入:第一个参数将是“格式”字符串,就像在原始格式方法中一样。第二个参数将是解析的字符串(请参见下面的示例)。不需要也不允许其他参数。

输出:格式的占位符对应提取的值的数组(或您的选择语言的等价形式)。

该占位符{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', '']
雅各布

然后可以输出deformat('{0}{1}{0}', 'ABBA') => ['', 'ABBA']吗?如果是这样,那么有一个便宜的解决方案,除非每个字符串至少出现两次。
xnor 2014年

您的廉价解决方案也适用deformat('{0}_{1}_{0}', 'A_BB_A')吗?
雅各布

哦,我知道了,我忘记了结果中的实际字符。我仍在努力解决算法难度。让我看看我是否可以组成一个非常反常的实例。
xnor

Answers:


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})使用多个表示形式,则中断-不强制执行它们的相等性,而是舍弃一个表示形式的所有匹配项。

mapKeys((0+).read)$如果匹配在10个以上的模式中的正确排序无关紧要,或者是否需要填充到相同长度,或者是否可以接受模式的字符串排序,则可以节省19个字符。无论如何,如果第一个参数中省略了模式,那么结果中也将省略它。

!!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 2014年

@proudhaskeller只会给read您留下模棱两可的类型。Haskell不知道将键读取为哪种可排序类型。+0强制使用一个数字,Haskell已可以从该数字中做出任意选择,并选择整数。
约翰·德沃夏克

2

Ruby,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

通过选择零长度匹配项(使之成为ABBA解决方案['', 'ABBA'])而不是问题的首选解决方案,可以节省5个字符。我选择将示例解释为规范的隐含部分。


1

Python,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 *),并返回带有键integer(模式)的hashmap,值是已标识的字符串。如果未找到任何内容或任何错误,则返回一个空的哈希映射。

用法:

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;
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.