Acrônimos recursivos

31

Objetivo

Da Wikipedia :

Um acrônimo recursivo é um acrônimo que se refere a si mesmo na expressão para a qual ele representa.

Seu objetivo é verificar se uma string é um acrônimo recursivo.

  • O acrônimo é a primeira palavra
  • As palavras não diferenciam maiúsculas de minúsculas, separadas por um único espaço.
  • A sequência especificada não contém pontuação nem apóstrofo.
  • Somente a primeira letra de cada palavra pode fazer parte da sigla.

Você também deve fornecer as palavras de função . Para simplificar, cada palavra pode ser considerada como uma palavra de função.

Exemplo

f("RPM Package Manager")         =>     { true, [] }
f("Wine is not an emulator")     =>     { true, ["an"] }
f("GNU is not Unix")             =>     { true, ["is"] }
f("Golf is not an acronym")      =>     { false }  
f("X is a valid acronym")        =>     { true, ["is","a","valid","acronym"] }  

Você pode dar um programa completo ou uma função.
A cadeia de entrada pode ser obtida de STDIN ou como argumento de função.
O resultado da saída pode ser verdadeiro / falso, 0/1, sim / não ...
A lista de palavras da função (qualquer formato da lista é válido) deve ser fornecida se e somente se for um acrônimo recursivo (mesmo que a lista esteja vazia) . Você não precisa preservar a capitalização das palavras de função.

Critérios de vitória

Este é um , o código mais curto vence.

Michael M.
fonte
4
Temos que preservar a capitalização das palavras de função?
algorithmshark
1
É aceitável ter uma lista de cadeias acompanhando um valor False ou não?
Undergroundmonorail
1
Como a própria lista de palavras codifica o valor booleano por sua presença, podemos omitir o valor booleano?
John Dvorak
5
Hurd é a sigla de Hird of Unix-Replacing Daemons. Hird significa Hurd of Interfaces Representing Depth. Por que os exemplos aqui não entendem isso e afirmam que não são acrônimos recursivos?
Konrad Borowski
3
@xfix, a wikipedia afirma que esses são acrônimos mutuamente recursivos .
Michael M.

Respostas:

7

GolfScript, 51 50 caracteres

{32|}%" "/(1>\{.1<2$1<={;1>}{\}if}/{]!}{]`1" "@}if

Provavelmente pode ser jogado mais. Recebe entrada em STDIN. O booleano é 0/1.

Teste on-line


Explicação:

{32|}%      # change everything to lower-case
" "/        # splits the string by spaces
(1>         # takes the first word out and removes the first letter
\           # moves the list of remaining words in front of the acronym word
{           # for every word:
  .1<2$1<=    # compares the first letter of the word with
              # the next unmatched letter of the acronym
  {;1>}       # if they are the same, discard the word and the now-matched letter
  {\}         # otherwise store the word in the stack
  if          # NB. if all letters have been matched, the comparison comes out as false
}/
{]!}        # if there are still unmatched letters, return 0 (`!` non-empty list)
{]`1" "@}   # otherwise, return 1, and display the list of function words
if
Volatilidade
fonte
22

Regex, sabor .NET, 62 bytes

(?i)(?<=^\w(?<c>\w)*)( \k<c>(?<-c>)\w+| (?<w>\w+))*$(?(c)(?!))

Você pode testá-lo aqui . Se a entrada for um acrônimo recursivo, isso produzirá uma correspondência e o grupo de captura wconterá todas as palavras de função. Caso contrário, não haverá correspondência.

Isso faz preservar a capitalização das palavras de função (mas corresponde caso insensível).

Infelizmente, o testador não exibe toda a pilha de um grupo de captura de chamada, mas se você usou-o em qualquer lugar .NET, o wgrupo iria conter todas as palavras de função em ordem.

Aqui está um trecho de código C # para provar que:

var pattern = @"(?i)(?<=^\w(?<c>\w)*)( \k<c>(?<-c>)\w+| (?<w>\w+))*$(?(c)(?!))";
var input = new string[] {
    "RPM Package Manager",
    "Wine is not an emulator",
    "GNU is not Unix",
    "Golf is not an acronym",
    "X is a valid acronym"
};

var r = new Regex(pattern);
foreach (var str in input)
{
    var m = r.Match(str);
    Console.WriteLine(m.Success);
    for (int i = 0; i < m.Groups["w"].Captures.Count; ++i)
        Console.WriteLine(m.Groups["w"].Captures[i].Value);
}

Aqui está uma explicação rápida. Estou usando os grupos de balanceamento do .NET para criar uma pilha das letras de sigla no grupo nomeado c, com esse snippet

^\w(?<c>\w)*

O truque é que eu preciso da segunda letra no topo da pilha e a última na parte inferior. Então eu coloquei tudo isso em um lookbehind que corresponde à posição após a sigla. Isso ajuda, porque o .NET corresponde à aparência da direita para a esquerda e, portanto, encontra a última letra primeiro.

Depois de obter essa pilha, ligo o resto da string palavra por palavra. A palavra começa com a letra no topo da pilha de acrônimos. Nesse caso, eu tiro essa letra da pilha:

 \k<c>(?<-c>)\w+

Caso contrário, eu igualo a palavra de qualquer maneira e empurro a wpilha que conterá todas as palavras de função:

 (?<w>\w+)

No final, certifico-me de que cheguei ao final da string com $e também verifiquei se todas as letras do acrônimo foram usadas, verificando se a pilha está vazia:

(?(c)(?!))

Teste em ideone.

Martin Ender
fonte
1
Ótima expressão regular, mas a pergunta afirma claramente "Você pode dar um programa completo ou uma função ".
Escova de dentes
4
@toothbrush Se o OP decidir desqualificar minha resposta com base nisso, que assim seja. Mas acho que posso afirmar que este é um programa completo na linguagem que é o sabor da expressão regular do .NET (não é uma linguagem completa de Turing e é um pouco complicada de executar, mas, mesmo assim, é uma linguagem). De qualquer forma, gosto do fato de ter resolvido isso com uma abordagem de pura regex e prefiro ter a resposta desqualificada do que destruir essa "elegância" (se você quiser), tornando-a "apenas uma resposta em C # usando regex "
Martin Ender
Tudo bem para mim. Eu só queria salientar se você perdeu.
Escova de dentes
1
Eu gosto disso. Regexes podem não ser uma linguagem de programação completa de Turing, mas acho que isso deve contar.
Paul Draper
@PaulDraper Na verdade, eu nem apostaria que o sabor do regex do .NET não fosse Turing completo ... os grupos de equilíbrio e os lookbehinds da direita para a esquerda são muito poderosos. E o PCRE, por exemplo, é conhecido por ser Turing completo (aquele com recursão, não tenho certeza de que as pilhas no .NET sejam suficientes para emular iterações arbitrárias).
Martin Ender
11

Python (158, sem regex)

Não é que eu não goste de expressões regulares. É que eu não os conheço.

def f(x):
 s=x.lower().split();w=list(s[0][1:]);s=s[1:];o=[]
 if not w:return 1,s
 [w.pop(0)if i[0]==w[0]else o.append(i)for i in s]
 return(0,)if w else(1,o)

Ah, eu também tinha uma versão não destruída:

def acronym(string):
    scentence = string.lower().split()
    word = scentence[0][1:]
    scentence = scentence[1:]
    over = []
    if not word: return 1, scentence
    for item in scentence:
        if item[0] == word[0]:
            word = word[1:]
        else:
            over.append(item)
    if word:
        return 0,
    return 1,over
ɐɔıʇǝɥʇuʎs
fonte
5

Python 2.7 - 131 126 bytes

def f(s):
 s=s.lower().split();a,f=list(s[0]),[]
 for w in s:f+=0*a.pop(0)if a and w[0]==a[0]else[w]
 return(0,)if a else(1,f)

Faz uma lista de letras na primeira palavra da sigla. Em seguida, para cada palavra na sequência completa, livre-se do primeiro elemento da lista que fizemos, se for o mesmo que a primeira letra dessa palavra. Caso contrário, adicione essa palavra à lista de palavras de função. Para saída, retorne not a(em python, qualquer lista que não seja a lista vazia é True-y, e a lista está vazia se for um acrônimo recursivo) e a lista se not a.

Agradeço ao @ace por me ajudar a corrigir um erro / salvar alguns bytes.

undergroundmonorail
fonte
No Python 2.7.3, chego SyntaxError: invalid syntaxao final da returnlinha.
User12205
@ace Huh, eu poderia jurar que funcionou quando o testei. Eu devo ter mudado alguma coisa e esqueci de testar novamente. Vou trabalhar em uma correção!
Undergroundmonorail
Você pode usar o for w in s:f+=0*a.pop(0)if a and w[0]==a[0]else[w]que é mais curto e não depende de guias. Quanto à returndeclaração, descobri 0if a else(1,f)qual é mais curta que o original.
User12205
Ah, e se você usar ponto e vírgula para colocar as duas primeiras instruções na mesma linha, você salvará um byte de recuo.
User12205
1
Eu descobri uma maneira de corrigir o erro, mas quando voltei aqui para postá-lo você tinha golfed-lo mais nos comentários: P
undergroundmonorail
3

Python - 154 caracteres

Primeira tentativa de código de golfe. Estou pensando que python não é a melhor linguagem para isso, considerando todas as palavras-chave longas. Além disso, não acho que essa função seja infalível. Funciona para a entrada do OP, mas tenho certeza de que poderia pensar em exceções.

def f(s):
    w=s.lower().split();r=list(w[0]);return(True,[x for x in w if x[0]not in r])if len(r)==1 or[x for x in[y[0]for y in w]if x in r]==r else False
Obversidade
fonte
Conto 156 caracteres (a nova linha e o caractere de tabulação contam), mas você pode reduzi-lo para 154 legitimamente removendo esses dois caracteres, pois nenhum deles é realmente necessário. Bem-vindo ao PPCG, btw. :)
undergroundmonorail
3

ECMAScript 6 (105 bytes):

f=s=>(r=(a=s.toUpperCase(i=1).split(' ')).map((w,c)=>c?a[0][i]==w[0]?(i++,''):w:''),a[0].length==i?1+r:0)

Digite a função no console do navegador Firefox e, em seguida, basta chamar a função, assim:

f('ABC Black Cats')     // 1,,
f('ABC is Black Cats')  // 1,IS,,
f('ABC Clapping Cats')  // 0
Escova dental
fonte
Não chega a cumprir as regras: The function words list ... must be given if and only if this is a recursive acronym. Isso os alertará independentemente.
MT0 27/05
@ MT0 OK. Eu não percebi esse requisito. Vou ver se consigo reescrevê-lo.
Escova de dentes
@ MT0 Atualizei o código agora.
Escova de dentes
2

Haskell - 287 bytes

Não é a entrada mais curta (ei, é Haskell, o que você esperava?), Mas ainda assim é muito divertido escrever.

import Data.Char
import Data.List
f""w=map((,)False)w
f _[]=[]
f(a:as)(cs@(c:_):w) 
 |toLower a==toLower c=(True,cs):f as w
 |True=(False,cs):f(a:as)w
g s=if(length$filter(fst)d)==length v
  then Just$map(snd)$snd$partition(fst)d 
  else Nothing
 where 
  w=words s
  v=head w
  d=f v w

Testado com

map (g) ["RPM Package Manager","Wine is not an emulator","GNU is not Unix","Golf is not an acronym","X is a valid acronym"]

Saída esperada

[Just [],Just ["an"],Just ["is"],Nothing,Just ["is","a","valid","acronym"]]

Ungolfed

import Data.Char
import Data.List

f :: String -> [String] -> [(Bool, String)]
f "" w = map ((,) False) w
f _ [] = []
f (a:as) ((c:cs):w) | toLower a == toLower c = (True, c:cs) : f as w
                    | otherwise = (False, c:cs) : f (a:as) w

g :: String -> Maybe [String]
g s = if (length $ filter (fst) d) == (length v)
          then Just $ map (snd) $ snd $ partition (fst) d 
          else Nothing
  where w = words s
        v = head w
        d = f v w
gxtaillon
fonte
2

JavaScript (ECMAScript 6) - 97 caracteres

f=x=>(r=(a=x.toLowerCase(i=0).split(' ')).filter(y=>y[0]!=a[0][i]||i-i++),i==a[0].length?[1,r]:0)

Testes:

f("RPM Package Manager")
[1, []]

f("GNU is not Unix")
[1, ["is"]]

f("X is an acronym")
[1, ["is", "an", "acronym"]]

f("Golf is not an acronym")
0

f("Wine is not an emulator")
[1, ["an"]]
MT0
fonte
1

Rebol - 133

f: func[s][w: next take s: split s" "y: collect[foreach n s[either n/1 = w/1[take w][keep n]]]reduce either/only w: empty? w[w y][w]]

Ungolfed:

f: func [s] [
    w: next take s: split s " "
    y: collect [
        foreach n s [
            either n/1 = w/1 [take w][keep n]
        ]
    ]
    reduce either/only w: empty? w [w y][w]
]

Testado com:

foreach t [
    "RPM Package Manager"  "Wine is not an emulator"  
    "GNU is not Unix"      "Golf is not an acronym"  
    "X is a valid acronym"
][probe f t]

Saída:

[true []]
[true ["an"]]
[true ["is"]]
[false]
[true ["is" "a" "valid" "acronym"]]
draegtun
fonte
1

Julia - 116 bytes

f(w)=(a=split(lowercase(w));L=1;A=a[];while a!=[];a[][1]==A[1]?A=A[2:]:(L=[L,a[]]);a=a[2:];A>""||return [L,a];end;0)

Menos Golfe:

f(w)=(
 a=split(lowercase(w))
 L=1
 A=a[]
 while a!=[]
  if a[][1]==A[1]
   A=A[2:]
  else
   L=[L,a[]]
  end
  a=a[2:]
  if !(A>"")
   return [L,a]
  end
 end
0)

O 0no final faz com que seja a saída 0. Caso contrário, ele gera uma matriz contendo 1seguidos pelas palavras da função. Por exemplo:

julia> f("RPM Package Manager")
1-element Array{Any,1}:
 1

julia> f("Golf is not an acronym")
0

julia> f("GNU is not Unix")
2-element Array{Any,1}:
 1    
  "is"

julia> f("X is a valid acronym")
5-element Array{Any,1}:
 1         
  "is"     
  "a"      
  "valid"  
  "acronym"
Glen O
fonte
1

Braquilog , 29 bytes

ḷṇ₁XhY∧X;0zpᵐz{ċ₂ˢ}ᵐZhhᵐcY∧Zt

Experimente online!

Emite as palavras de função através da variável de saída se a entrada for um acrônimo recursivo e falhará se não for.

   X                             X is
ḷ                                the input lowercased
 ṇ₁                              and split on spaces,
    hY                           the first element of which is Y
      ∧                          (which is not X).
       X  z                      X zipped
        ;0                       with zero,
           pᵐ                    with all pairs permuted (creating a choicepoint),
             z                   zipped back,
              {   }ᵐ             and with both resulting lists
               ċ₂ˢ               losing all non-string elements,
                    Z            is Z.
                      hᵐ         The first elements of the elements of
                    Zh           the first element of Z
                        cY       concatenated are Y
                          ∧      (which is not Z).
                           Zt    The last element of Z is the output.

Sem precisar produzir as palavras de função (tratando isso como um puro ), ele sai para apenas 12 bytes, porque ∧Ztpode ser descartado para -3, Ypode ser substituído .por -1 e, o mais importante, ;0zpᵐz{ċ₂ˢ}ᵐZhpode ser substituído por for um gritante -13:ḷṇ₁Xh.∧X⊇hᵐc

String não relacionada
fonte
0

Cobra - 187

def f(s as String)
    l=List<of String>(s.split)
    a=l[0]
    l.reverse
    o=0
    for c in a,for w in l.reversed
        if c==w[0]
            l.pop
            o+=1
            break
    x=o==a.length
    print x,if(x,l,'')
Furioso
fonte
0

Ruby - 173

Poderia ser melhor...

 f=->s{a=[];o={};s=s.split;r=true;s[0].each_char{|c|s.each{|w| w[0]=~/#{c}/i?(o[c]=1;a<<w if o[c]):(o[c]=0 if !o[c])}};r,a=false,s if o.values&[0]==[0];!r ?[r]:[r,(s-(a&a))]}

Chamando a função:

p f.call('RPM Package Manager')
p f.call('Wine is not an emulator')
p f.call("GNU is not Unix")
p f.call("Golf is not an acronym")
p f.call("X is a valid acronym")

Saída:

[true, []]
[true, ["an"]]
[true, ["is"]]
[false]
[true, ["is", "a", "valid", "acronym"]]
cebolinha
fonte
0

Java - 195

Infelizmente, o Java não possui suporte à tupla.

Portanto, essa é uma classe que armazena o booleano em 'b' e a lista de palavras da função em 'x'.

Aqui, a função é o construtor da classe.

static class R{boolean b;String[]x;R(String s){String v=" ",i="(?i)",f=s.split(v)[0],r=i+f.replaceAll("(?<=.)",".* ");if(b=(s+=v).matches(r))x=(s.replaceAll(i+"\\b["+f+"]\\S* ","")+v).split(v);}}

Teste

public class RecursiveAcronyms {
public static void main(String args[]) {
    String[] tests = {
            "RPM Package Manager",
            "Wine is not an emulator",
            "GNU is not Unix",
            "Golf is not an acronym",
            "X is a valid acronym"
        };
    for (String test:tests) {
        R r = new R(test);
        System.out.print(r.b);
        if (r.b) for (String s:r.x) System.out.print(" "+s);
        System.out.print("\n");
    }
}
static class R{boolean b;String[]x;R(String s){String v=" ",i="(?i)",f=s.split(v)[0],r=i+f.replaceAll("(?<=.)",".* ");if(b=(s+=v).matches(r))x=(s.replaceAll(i+"\\b["+f+"]\\S* ","")+v).split(v);}}}
Vetorizado
fonte
C # tem tuplas, mas eu vim com isso enquanto trabalhava na minha solução: basta retornar string[]: nullsimplesmente significa falso, vazio significa verdadeiro e nelementos significa verdadeiro com npalavras de função.
Num Lock
Eu também gostaria de fazer isso. No entanto, o OP especifica que o booleano deve ser fornecido independentemente. Veja a resposta ao comentário de Jan Dvorak.
Vetorizado
Não ligo para os comentários, pois não consigo identificar uma edição resultante na postagem original. E mesmo se eu fiz , claramente diz apenas " especificar o booleano ". E mesmo na resposta em si, diz " O resultado da saída pode ser verdadeiro / falso, 0/1, sim / não ... +", que eu poderia estender nas reticências por "* null / not null " ...
Num Bloquear
0

Awk - 145

awk -v RS=' ' '{c=tolower($0)};NR==1{w=c};{t=substr(c,1,1)!=substr(w,NR-s,1);if(t){f=f" "c;s++};a=a||t};END{print a&&(s>NR-length(w))?"N":"Y|"f}'

Teste:

$ cat gcp.sh
#!/bin/sh
f() {
echo "$1:"
echo "$1"|awk -v RS=' ' '{c=tolower($0)};NR==1{w=c};{t=substr(c,1,1)!=substr(w,NR-s,1);if(t){f=f" "c;s++};a=a||t};END{print a&&(s>NR-length(w))?"N":"Y|"f}'
}
f "RPM Package Manager"
f "Wine is not an emulator"
f "Wine is not an appropriate emulator"
f "GNU is not Unix"
f "Golf is not an acronym"
f "Go is not an acronym"
f "Go is a valid acronym OK"
f "X is a valid acronym"
f "YAML Ain't Markup Language"

$ ./gcp.sh
RPM Package Manager:
Y|
Wine is not an emulator:
Y| an
Wine is not an appropriate emulator:
Y| an appropriate
GNU is not Unix:
Y| is
Golf is not an acronym:
N
Go is not an acronym:
N
Go is a valid acronym OK:
Y| is a valid acronym
X is a valid acronym:
Y| is a valid acronym

YAML Ain't Markup Language:
Y|
hjk
fonte
0

Café - 144

z=(a)->g=" ";b=a.split g;c=b[0];d=[];(d.push(e);g++)for e,f in b when e[0].toLowerCase()!=c[f-g].toLowerCase();if(g+c.length==f)then{1,d}else{0}

Ligue com, por exemplo: z "GNU is not Unix"

O JS compilado:

var z;
z = function(a) {
  var b, c, d, e, f, g, _i, _len;
  g = " ";
  b = a.split(g);
  c = b[0];
  d = [];
  for (f = _i = 0, _len = b.length; _i < _len; f = ++_i) {
    e = b[f];
    if (e[0].toLowerCase() !== c[f - g].toLowerCase()) {
      d.push(e);
      g++;
    }
  }
  if (g + c.length === f) {
    return {
      1: 1,
      d: d
    };
  } else {
    return {
      0: 0
    };
  }
};

Ele divide a string em palavras e depois percorre cada palavra. Se o primeiro caractere da palavra não corresponder ao próximo no acrônimo, a palavra será armazenada. Um contador ( g) é usado para rastrear quantas palavras foram puladas. Se o número de palavras ignoradas mais o comprimento da sigla corresponder à duração da frase, ela corresponderá, retorne 1 e as palavras ignoradas. Caso contrário, não era válido, então retorne 0.

Johno
fonte
0

C # - 234

Tuple<bool,string[]> f(string w) {var l=w.ToLower().Split(' ');var o=new List<string>();int n=0;var a=l[0];foreach(var t in l){if(n>=a.Length||a[n]!=t[0])o.Add(t);else n++;}var r=n>=a.Length;return Tuple.Create(r,r?o.ToArray():null);}
Grax32
fonte
0

Python (108)

l=raw_input().lower().split()
a=l[0]
e=[]
for w in l:d=w[0]!=a[0];a=a[1-d:];e+=[w]*d  
b=a==''
print b,b*`e`
xnor
fonte