Implementar la finalización de la pestaña

31

La finalización de tabulación es una característica útil que completa automáticamente los comandos escritos parcialmente. Lo vas a implementar.

Por ejemplo, si los comandos disponibles fueran ['apply','apple','apple pie','eat'], entonces ase completarían en appl, ya que todos los comandos que comienzan con atambién comienzan con appl.

De entrada y salida

Debe ingresar una cadena, A y un conjunto de cadenas, B.

Debe generar el prefijo común más largo de todos los B que comienza con A.

  • Si ninguna de las opciones comienza con A, entonces devuelve A
  • Puede suponer que B no está vacío, y que todas las cadenas no están vacías
  • No puede suponer que ninguna de las opciones comience con A, ni que el prefijo común sea más largo que A
  • Puede ser sensible a mayúsculas o minúsculas.
  • Solo necesita manejar ASCII imprimible
  • Los elementos integrados que realizan esta tarea explícitamente están permitidos

Casos de prueba:

'a'       ['apply','apple','apple pie','eat'] => 'appl'
'a'       ['apple pie']                       => 'apple pie'
'apple'   ['eat','dine']                      => 'apple'
'program' ['programa','programb']             => 'program'
'*%a('    ['*%a()-T>','*%a()-T<','@Da^n&']    => '*%a()-T'
'a'       ['abs','absolute','answer']         => 'a'
'a'       ['a','abs']                         => 'a'
'one to'  ['one to one','one to many']        => 'one to '

Tenga en cuenta el espacio final en el último caso de prueba

Este es un , ¡así que haga sus respuestas lo más breve posible!

Nathan Merrill
fuente
¿Podría agregar un ejemplo con caracteres ASCII no alfabéticos e imprimibles para la posteridad?
Conor O'Brien
Más ejemplos con caracteres no alfabéticos no podrían doler. Acabo de eliminar mi respuesta porque me di cuenta de que se rompió con entradas que contienen \​o '.
Dennis
No estoy seguro de cómo representar 'en un ejemplo. Si uso "para las cadenas, entonces las cadenas son diferentes a otros ejemplos.
Nathan Merrill
Ese es exactamente el problema que tuvo mi respuesta. : P
Dennis

Respuestas:

10

JavaScript (ES6), 75 bytes

(s,a)=>/^(.*).*(\n\1.*)*$/.exec(a.filter(e=>e.startsWith(s)).join`
`)[1]||s

Explicación: Filtra en todos los prefijos coincidentes, luego se une con nuevas líneas y coincidencias contra una expresión regular que encuentra el prefijo común más largo de todas las líneas. Si no hay prefijos, la expresión regular devuelve una cadena vacía, en cuyo caso simplemente devolvemos la cadena original.

Neil
fuente
Puede reemplazar e.startsWith(s)con e.match("^"+s)un byte de Curry salvará otro
Shaun H
@ShaunH No puedo usar matchcon ASCII imprimible arbitrario.
Neil
Oh, regex y personajes de control. todavía puede ganarse (s,a)=>as=>a=>
Shaun H
7

Jalea , 14 12 bytes

ḣJ$€ċÐff\ṪṪȯ

Pruébalo en línea! o verificar todos los casos de prueba .

Cómo funciona

ḣJ$€ċÐff\ṪṪȯ  Main link. Left argument: B. Right argument: A

  $€          Convert the two links to the left into a monadic chain and apply it
              to each string s in B.
 J              Generate the indices of s, i.e., [1, ..., len(s)].
ḣ               Head; for each index i, take the first i characters of s.
              This generates the prefixes of all strings in B.
     Ðf       Filter; keep prefixes for which the link to the left returns 1.
   ċ            Count the number of times A appears in the prefixes of that string.
       f\     Do a cumulative (i.e., keeping all intermediate values) reduce by
              filter, keeping only common prefixes. f/ is a more obvious choice,
              but it errors on an empty array, i.e., when A isn't a prefix of any
              string in B.
         Ṫ    Tail; take the last prefix array (if any) or return 0.
          Ṫ   Tail; take the last common prefix (if any) or return 0.
           ȯ  Logical OR (flat); replace 0 with A, leave strings untouched.
Dennis
fuente
6

Pyth, 14 13 bytes

Gracias a @isaacg por -1 byte

.xe@F/#z._MQz

Un programa que toma la lista de cadenas, y luego la cadena, en STDIN e imprime el resultado.

Verificar todos los casos de prueba

Cómo funciona

.xe@F/#z._MQz  Program. Inputs: Q, z
        ._MQ   Map prefixes over Q
     /#z       Filter that by count(z)>0, removing the prefixes corresponding to elements
               in Q that do not start with z
   @F          Fold intersection over that. This yields all the common prefixes
  e            Yield the last element of that, giving the longest common prefix, since the
               prefixes are already sorted by length
.x             But if that throws an exception since no elements of Q start with z:
            z  Yield z instead
               Implicitly print
TheBikingViking
fuente
1
f}zT=>/#z
isaacg
5

PowerShell v3 +, 112 bytes

param($a,$b)if($c=@($b-like"$a*")){([char[]]$c[0]|%{($i+="$_")}|?{($c-like"$_*").count-eq$c.count})[-1]}else{$a}

Toma la entrada como una cadena $ay una matriz de cadenas $b. Utiliza el -likeoperador para extraer esos elementos de los $bque comienza (sin distinción entre mayúsculas y minúsculas) $a, explícitamente convertirlos como una matriz @(...)(ya que el resultado podría ser una coincidencia como escalar, en cuyo caso la indexación falla más tarde) y almacenar esa matriz en $c.

Eso forma la ifcláusula. Si no hay nada en $c(es decir, nada comienza con $a, por lo que la matriz está vacía), entonces la salida $acon el else. De lo contrario ...

Emitimos el primer elemento de $ccomo una charmatriz y recorremos cada elemento, concatenando cadenas con el anterior $iy colocando las cadenas en la tubería a través de paréntesis encapsulantes. Esos se filtran |?{...}(la Where-Objectcláusula) para verificar que el valor .countde ual $ces -eqel .countde las cosas $cque son -likela subcadena (es decir, la subcadena coincide con todo en $ c). Como estamos construyendo nuestras subcadenas en orden más corto a más largo, necesitamos la última [-1]de las cadenas resultantes.

Casos de prueba

PS C:\Tools\Scripts\golfing> $tests=@('a',@('apply','apple','apple pie','eat')),@('a',@('apple pie')),@('apple',@('eat','dine')),@('program',@('programa','programb')),@('one to',@('one to one','one to many')),@('*%a(',@('*%a()-T>', '*%a()-T<', '@Da^n&'))

PS C:\Tools\Scripts\golfing> $tests|%{""+$_[0]+" ("+($_[1]-join',')+") -> "+(.\implement-tab-completion.ps1 $_[0] $_[1])}
a (apply,apple,apple pie,eat) -> appl
a (apple pie) -> apple pie
apple (eat,dine) -> apple
program (programa,programb) -> program
one to (one to one,one to many) -> one to 
*%a( (*%a()-T>,*%a()-T<,@Da^n&) -> *%a()-T
AdmBorkBork
fuente
4

Python 2, 122 bytes

s=input();l=[x for x in input()if x[:len(s)]==s]or[s];i=len(l[0])
while len(l)>1:i-=1;l=set(x[:i]for x in l)
print l.pop()

Programa completo; toma una cadena y una lista de stdin exactamente como se indica en los ejemplos, excepto que las entradas deben estar en líneas separadas.

Verificar todos los casos de prueba

DLosc
fuente
¿Por qué en l.pop()lugar de l[-1]?
Cyoce
@Cyoce Porque lgeneralmente es un setpunto en ese punto, que no permite la indexación (estar desordenado). (Afortunadamente, ambos conjuntos y listas admiten pop().)
DLosc
3

Perl, 54 bytes

Incluye +2 para -Xp(se puede combinar con -e) y +3 para -i(no se puede combinar)

Dé el diccionario en STDIN y la palabra después de la -iopción, por ejemplo:

perl -ia -Xpe '/^\Q$^I\E.*?(?{$F[$a{$&}++]=$&})^/}{$_=pop@F||$^I'
apply
apple
apple pie
eat
^D

Solo el código:

/^\Q$^I\E.*?(?{$F[$a{$&}++]=$&})^/}{$_=pop@F||$^I
Ton Hospel
fuente
3

Perl, 61 bytes

Incluye +2 para -0p

Ejecute con la primera palabra seguida de las palabras del diccionario en STDIN:

tabcompletion.pl
a
apply
apple
apple pie
eat
^D

tabcompletion.pl:

#!/usr/bin/perl -0p
/^(.+)
((?!\1).*
)*(\1.*).*
((?!\1).*
|\3.*
)*$|
/;$_=$3||$`
Ton Hospel
fuente
2

Python 2, 112 bytes

lambda n,h:[a.pop()for a in[{s[:-i]for s in h if s.find(n)==0}for i in range(-len(`h`),0)]+[{n}]if len(a)==1][0]
Lynn
fuente
2

Haskell, 67 bytes

(a:b)?(c:d)|a==c=a:(b?d)
_?_=""
s%l=foldr1(?)$max[s][x|x<-l,x?s==s]

La función auxiliar ?encuentra el prefijo común más largo de dos cadenas al tomar recursivamente el primer carácter siempre que sea el mismo para ambas cadenas y las cadenas no estén vacías.

La función principal %primero mantiene solo las cadenas en la lista que comienzan con la dada s, marcada por el prefijo común más largo con sbeing s. Para manejar que no haya competiciones válidas, se agrega sa un resultado vacío a través de max. Luego, encuentra el prefijo común más largo de esos al plegar la función binaria ?.

xnor
fuente
2

Python 2, 75 bytes

import os
lambda s,x:os.path.commonprefix([t for t in x if s<=t<s+'ÿ'])or s

Gracias a @xnor por sugerir el incorporado, utilizado originalmente por @BetaDecay en esta respuesta .

Para fines de puntuación, ÿse puede reemplazar con un byte DEL. Pruébalo en Ideone .

Dennis
fuente
1

D, 88 bytes

S f(S)(S p,S[]q){try p=q.filter!(a=>a.startsWith(p)).fold!commonPrefix;catch{}return p;}

Uso:

assert(f("a", ["apply","apple","apple pie","eat"]) ==  "appl");

El código simplemente elimina todos los elementos qque no comienzan p, luego calcula la subsecuencia inicial común más grande de los elementos restantes.

Los parámetros de plantilla nos ahorran dos repeticiones de stringy una de auto. El mal uso de la excepción nos permite evitar la variable temporal y condicional que de otro modo sería necesaria para manejar el caso en el que no hay elementos de qinicio p.

Rayo
fuente
1

Python 2, 107 102 bytes

s,x=input();r='';q=1
for c in zip(*[t for t in x if s<=t<s+'ÿ']):q/=len(set(c));r+=c[0]*q
print r or s

Para fines de puntuación, ÿse puede reemplazar con un byte DEL. Pruébalo en Ideone .

¡Gracias a @xnor por guardar 5 bytes!

Dennis
fuente
Con lo os.path.commonprefix que encontró Beta Decay , puede hacer que haga el trabajo por usted.
xnor
Wow, eso ahorra muchos bytes. ¿Estás seguro de que no quieres publicar eso tú mismo?
Dennis
No me sentiría bien publicarlo yo mismo, ya que es solo la idea de Beta Decay combinada con tu respuesta.
xnor
Para su solución, parece un poco más corto iterar for c in ...directamente y terminar con un error después de imprimir como if len(set(c))>1:print r or s;_.
xnor
Creo que fallaría si x es una matriz singleton.
Dennis
1

PHP, 167 160 157 152 bytes

<?for($r=preg_grep("$^".preg_quote($s=$_GET[s])."$",$a=$_GET[a]);$r[0]>$s&&preg_grep("$^".preg_quote($t=$s.$r[0][strlen($s)])."$",$a)==$r;)$s=$t;echo$s;

Podría ahorrar 3 bytes más asignando variables con preg_grepy preg_quote, pero eh.

Descompostura

for(
    // find items in $a that start with $s
    $r=preg_grep("$^".preg_quote($s=$_GET[s])."$",$a=$_GET[a]);
    // while the first match is longer than $s
    $r[0]>$s
    // and appending the next character of the first match
    &&preg_grep("$^".preg_quote($t=$s.$r[0][strlen($s)])."$",$a)
    // does not change the matches
    ==$r
;)
    // keep appending
    $s=$t;
return$s;
Tito
fuente
1

PHP, 156 bytes

con mucha ayuda de Titus Gracias

<?foreach($_GET[t]as$v)if(strstr($v,$s=$_GET[s])==$v)$r[]=$z=$v;for(;$i++<strlen($z);){$s=substr($z,0,$i);foreach($r as$x)if($x[$i]!=$z[$i])break 2;}echo$s;

PHP, 199 bytes

32 Bytes guardados por Titus con array_unique

<?foreach($_GET[t]as$v)if(strstr($v,$s=$_GET[s])==$v)$r[]=$v;for(;$i++<strlen($r[0]);$a=[]){foreach($r as$x)$a[]=substr($x,0,$i);if(count($r)==count($a)&count(array_unique($a))<2)$s=$a[0];}echo$s;

Sé que la Solución Regex de Titus fue más corta hasta que Titus me ayudó a mejorar mi camino. Tal vez la forma que encontré es interesante para ti

Jörg Hülsermann
fuente
1
1) Reemplace $zcon $spara arreglar el apple, [eat,dine]caso. 2) $l=es obsoleto; No usas esa variable. (-2) 3) $i++<$mes más corto que ++$i<=$m. (-1) 4) substr($x,0,$i);es más corto que str_split($x,$i)[0]. (-3) 5) Puedes poner $r[]=$vdentro del strlen. (-5)
Tito
1
6) <2es más corto que ==1. (-1) 7) Se podría utilizar strstren el primer bucle: strstr($v,$s)==$v. (-3)
Tito
1
Permítanme parafrasear que: 5) Se puede combinar $r[]=$v;$m=max($m,strlen($v));con $m=max($m,strlen($r[]=$v));y soltar los curlys. Esto no toca la condición.
Tito
1
Pensándolo bien, no necesitas $mnada. Todo lo que necesita es algo que sea> = la longitud mínima de los reemplazos. El nuevo 5) Reemplazar {$r[]=$v;$m=max($m,strlen($v));}con $r[]=$v;}y <$mcon <strlen($r[0])(-13)
Tito
1
¡Excelente! Y acabo de encontrar otro golf: 9) $r[]=$z=$v;en el primer bucle y {$s=substr($z,0,$i);foreach($r as$x)if($x[$i]!=$z[$i])break 2;}para el segundo (-3)
Titus
1

Retina, 60 bytes

^(.*)(\n(?!\1).*)*(\n(\1.*)).*(\n((?!\1)|\4).*)*$
$4
s`\n.*

La nueva línea final es significativa. Toma la entrada como la cadena en una línea y luego cada palabra en una línea separada (¡pero sin nueva línea final!). Funciona de manera similar a mi respuesta de JavaScript haciendo coincidir el prefijo común más largo de todas las líneas que comienzan con la cadena en la primera línea. Si no encuentra uno, simplemente borra todas las palabras.

Neil
fuente
0

Scala, 119 bytes

def f(s:String,a:Seq[Char]*)=a filter(_ startsWith s)reduceOption(_ zip _ takeWhile(t=>t._1==t._2)map(_._1))getOrElse s

Sin golf:

def tabComplete(input: String, options: Seq[Char]*) = {
  options.
  filter((x: String) => x.startsWith(input)).
  reduceOption((x: Seq[Char], y: Seq[Char]) =>
    x.zip(y).
    takeWhile((t: (Char, Char)) => t._1 == t._2).
    map((t: (Char, Char)) => t._1)
  ).getOrElse(input)
}

Explicación:

def g(s:String,a:Seq[Char]*)= //define a method g with a string and a vararg array of strings as parameter
  a filter(_ startsWith s)    //filter the options to contains only elements starting with the input
  reduceOption(               //if the filtered array is nonempty, reduce it: 
    _ zip _                     //zip two elements together
    takeWhile(t=>t._1==t._2)    //take the tuples while they contain the same char
    map(_._1)                   //take the first element from each tuple
  )getOrElse s                //else return the input
corvus_192
fuente
0

05AB1E , 14 bytes

ʒIÅ?}€ηøʒË}‚˜θ

Pruébelo en línea o verifique todos los casos de prueba .

Explicación:

ʒ   }           # Filter the (implicit) input-list
 IÅ?            #  Does it start with the (second) input-string
                #   i.e. ["codex","bla","codegolf"] and "c" → ["codex","codegolf"]
     €η         # Then take the prefixes of every remaining string
                #  → [["c","co","cod","code","codex"],
                #     ["c","co","cod","code","codeg","codego","codegol","codegolf"]]
       ø        # Zip/transpose; swapping rows/columns
                #  → [["c","c"],["co","co"],["cod","cod"],["code","code"],["codex","codeg"]]
        ʒ }     # Filter:
         Ë      #  Only keep sublists which only contain the same substrings
                #   → [["c","c"],["co","co"],["cod","cod"],["code","code"]]
               # Pair it with the (second implicit) input
                #  → ["c",["c","c"],["co","co"],["cod","cod"],["code","code"]]
                # (workaround if nothing in the input-list starts with the input-string)
            ˜   # Flatten this list
                #  → ["c","c","c","co","co","cod","cod","code","code"]
             θ  # And only leave the last item (which is output implicitly as result)
                #  → "code"
Kevin Cruijssen
fuente
0

Gaia , 12 bytes

e…¦&⊢…Ė⁇_+ₔ)

Pruébalo en línea!

Toma la entrada como B, luego A.

e		| eval B as list of strings
 …¦		| take prefixes of each string
   &⊢		| reduce by set intersection
     …		| take list prefixes of each.
      Ė⁇	| Keep only those with A as an element
	_	| flatten
	 +ₔ	| add A to the beginning of the list
	   )	| take the last element
Giuseppe
fuente