Interpretar rangos sueltos

13

Interpretar rangos sueltos

ListSharp es un lenguaje de programación interpretado que tiene muchas características, una de esas características es un creador de rango basado en 1 índice que funciona así:

Defina un rango como (INT) TO (INT)o solo (INT)donde ambos o el int único pueden ir del valor mínimo al máximo int32

Entonces puede usar esos rangos para extraer elementos de una matriz sin temor a sobrepasar sus límites


por lo tanto:

1 TO 5 genera: {1,2,3,4,5}

3 genera: {3}

Los rangos se pueden sumar usando el ANDoperador

1 TO 5 AND 3 TO 6 genera: {1,2,3,4,5,3,4,5,6}

recuerda que esto también funciona con números negativos

3 TO -3 genera: {3,2,1,0,-1,-2,-3}


El desafío es el siguiente:

Entrada

Una matriz de caracteres y la cláusula de rango previamente definida como una cadena

Salida

Los elementos en las ubicaciones del rango basadas en 1 índice (los índices no existentes / negativos se traducen en un carácter vacío)


Cómo ganar

Como un desafío de , se supone que debes crear el programa con el menor recuento de bytes para ganar


Se ha señalado que los caracteres vacíos no existen, por lo tanto, debe ignorarlos (solo los mostré aquí para que sea más fácil de entender, pero confundió a las personas)

Casos de prueba:

input array is:
{'H','e','l','l','o',' ','W','o','r','l','d'}

range clause:
"1 TO 3" => "Hel"
"5" => "o"
"-10 TO 10" => "Hello Worl"
"0 AND 2 AND 4" => "el"
"8 TO 3" => "oW oll"
"-300 AND 300" => ""
"1 TO 3 AND 3 TO 1" => "HelleH"
"-20 TO 0 AND 1 AND 4" => "Hl"
downrep_nation
fuente
3
¿Se nos permite usar 0-index en lugar de 1-index como cadena de entrada? Entonces la cláusula range se convierte en "0 TO 2"=> {'H', 'e', 'l'}?
Kevin Cruijssen
La tabla ASCII no tiene un carácter vacío (excluyendo los no imprimibles). ¿Qué hay de malo en usar el espacio?
adrianmp
una matriz de caracteres es básicamente una cadena, para los mismos caracteres vacíos apenas no pueden imprimir o devueltos
downrep_nation
1
Además, ¿será, por ejemplo, 3 TO 3una entrada y cuál es la salida esperada?
Jordan
1
Necesita algunos casos de prueba para ANDing rangos múltiples. Además, no respondió si podemos usar la indexación basada en cero, que es estándar en la mayoría de los idiomas.
mbomb007

Respuestas:

5

Python 2 - 239 211 210 bytes

¡Gracias a @ mbomb007 y @Cyoce por seguir jugando al golf con esta solución!

def r(s):
 t=[]
 for x in s.split("AND"):
  if"T"in x:a=map(int,x.split("TO"));b=2*(a[0]<a[1])-1;t+=range(a[0],a[1]+b,b)
  else:t+=int(x),
 return t
lambda p,v:[(['']+p+['']*max(map(abs,r(v))))[x]for x in r(v)]

Enfoque directo. Probé generadores y una versión recursiva, pero no pudieron superar lo simple para cada ciclo. Soy un novato en el golf, así que lo más probable es que esto pueda mejorarse bastante. Además, la falla principal de este fragmento es que el rango como un objeto de lista se calcula nuevamente cada vez que se recupera un elemento de la matriz de caracteres (ver última línea, comprensión de la lista). Esto significa que r(s)se ejecuta len(r(s)) + 1veces.

Código sin golf:

def find_range(string):
    result = []

    # Split at each AND and look at each element separately
    for element in string.split("AND"):
        # Element is just a number, so add that number to the result list
        if "TO" not in string:
            result += [int(string)]

        # Generate a list with all the values in between the boundaries 
        # (and the boundaries themselves, of course) and append it to the result list
        else:
            boundaries = map(int, string.split("TO"))
            ascending = boundaries[0] < boundaries[1]

            # range(start, stop, step) returns [start, ..., stop - 1], so extend the stop value accordingly
            # range(8, 3, 1) returns just [], so choose respective step (negative for a descending sequence)
            result += range(boundaries[0], boundaries[1] + (1 if ascending else -1), 1 if ascending else -1)

# Make the char array 1-indexed by appending an empty char in 0th position
# Add enough empty chars at the end so too large and negative values won't wrap around
interpret = lambda chars, range_expr: [(['']+chars+['']*max(map(abs, find_range(range_expr))))[x] for x in find_range(range_expr)]

Casos de prueba:

c = list("Hello World")
print interpret(c, "1 TO 3")
print interpret(c, "5")
print interpret(c, "-10 TO 10")
print interpret(c, "0 AND 2 AND 4")
print interpret(c, "8 TO 3")
print interpret(c, "-300 AND 300")

Salida:

['H', 'e', 'l']
['o']
['', '', '', '', '', '', '', '', '', '', '', 'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l']
['', 'e', 'l']
['o', 'W', ' ', 'o', 'l', 'l']
['', '']
1Darco1
fuente
Gran primera respuesta! Bienvenido a PPCG!
mbomb007
Puede beneficiarse de ver Consejos para jugar golf en Python . Puede reemplazar 2 espacios para doble sangría con una sola pestaña. Puede poner el código siguiendo el ifen la misma línea y separarlos con punto y coma. Y quitar el espacio adentro [x] for. Además, 1if b else-1se puede reemplazar con b and 1or-1o 2*bool(b)-1para guardar un byte.
mbomb007
¡Gracias! Ya miré y (intenté) usar.d algunos de ellos. Volveremos a revisar todas las respuestas más tarde. :)
1Darco1
Y creo que puedes usar un nombre sin nombre lambda, ya que no es recursivo.
mbomb007
1
t+=[int(x)]canbecomet+=int(x),
Cyoce
3

Groovy ( 99 97 Bytes)

{n,v->Eval.me("[${n.replaceAll(" TO ","..").replaceAll(" AND ",",")}]").flatten().collect{v[it]}}

Pruébelo aquí: https://groovyconsole.appspot.com/edit/5155820207603712

Explicación:

  • .replaceAll(" TO ","..") - Reemplace el to con un rango tradicional.
  • .replaceAll(" AND ", ",") - Reemplace todos los ands con una coma.
  • "[${...}]" - Rodéelo con la notación "lista" en Groovy.
  • Eval.me(...) - Evaluar la cadena como código Groovy.
  • .flatten() - Acoplar la mezcla de matriz 2D y matriz 1D en una matriz 1D.
  • .collect{v[it]} - Recoge los índices de la matriz en una sola estructura.

Aquí hay una solución de 115 113 bytes que elimina nulos de la salida: https://groovyconsole.appspot.com/edit/5185924841340928

Aquí hay una solución de 117 bytes si dice que DEBE indexarse ​​en 1 en lugar de 0: https://groovyconsole.appspot.com/edit/5205468955803648

Si quiere que cambie el original por el byte 113/117, avíseme.

Urna de pulpo mágico
fuente
Si no le gusta que use "nulos" para los caracteres que no están allí, +5 bytes al final para "-nulo", que elimina todos los nulos del conjunto.
Magic Octopus Urn
1
Me encanta esta inteligente coincidencia
downrep_nation
Sin embargo, aprendí algo de esto, nunca supe que Groovy tenía Eval.me(...)hasta ahora; dado que usarlo en la práctica sería ridículamente inseguro, aún es algo genial de saber.
Urna de pulpo mágico
2

C #, 342 bytes

a=>r=>{var s=r.Replace(" AND","").Replace(" TO ","|").Split();int b=a.Length,i=0,n,m,j,o;var c=new List<char>();for(;i<s.Length;){var d=s[i++].Split('|');if(d.Length<2)c.Add(b<(n=int.Parse(d[0]))||n<1?' ':a[n-1]);else{o=(m=int.Parse(d[0]))<(n=int.Parse(d[1]))?1:-1;for(j=m;o>0?j<=n:j>=n;j+=o)c.Add(b<j||j<1?' ':a[j-1]);}}return c.ToArray();};

Método sin golf:

static char[] f(char[] a, string r)
{
    var s=r.Replace(" AND","").Replace(" TO ","|").Split();
    int b=a.Length,i=0,n,m,j,o;
    var c=new List<char>();
    for(;i<s.Length;)
    {
        var d=s[i++].Split('|');
        if(d.Length<2)
            c.Add(b<(n=int.Parse(d[0]))||n<1?' ':a[n-1]);
        else
        {
            o=(m=int.Parse(d[0]))<(n=int.Parse(d[1]))?1:-1;
            for(j=m;o>0?j<=n:j>=n;j+=o)
                c.Add(b<j||j<1?' ':a[j-1]);
        }
    }

    return c.ToArray();
}

Programa completo con casos de prueba:

using System;
using System.Collections.Generic;

namespace InterpretLooseRanges
{
    class Program
    {
        static void PrintCharArray(char[] a)
        {
            for (int i=0; i<a.Length; i++)
                Console.Write(a[i]);
            Console.WriteLine();
        }

        static void Main(string[] args)
        {
            Func<char[],Func<string,char[]>>f= a=>r=>{var s=r.Replace(" AND","").Replace(" TO ","|").Split();int b=a.Length,i=0,n,m,j,o;var c=new List<char>();for(;i<s.Length;){var d=s[i++].Split('|');if(d.Length<2)c.Add(b<(n=int.Parse(d[0]))||n<1?' ':a[n-1]);else{o=(m=int.Parse(d[0]))<(n=int.Parse(d[1]))?1:-1;for(j=m;o>0?j<=n:j>=n;j+=o)c.Add(b<j||j<1?' ':a[j-1]);}}return c.ToArray();};

            char[] ar = {'H','e','l','l','o',' ','W','o','r','l','d'};

            PrintCharArray(f(ar)("1 TO 3"));
            PrintCharArray(f(ar)("5"));
            PrintCharArray(f(ar)("-10 TO 10"));
            PrintCharArray(f(ar)("0 AND 2 AND 4"));
            PrintCharArray(f(ar)("8 TO 3"));
            PrintCharArray(f(ar)("-300 AND 300"));
        }
    }
}

Una solución ingenua, usando una lista de caracteres, que se usa ' 'como un personaje vacío y hace el trabajo. Con la esperanza de mejorar pronto.

adrianmp
fuente
2

Scala, 165 bytes

(s:String,r:String)=>r split "AND"map(_ split "TO"map(_.trim.toInt))flatMap{case Array(a,b)=>if(a<b)a to b else a to(b,-1)
case x=>x}map(i=>s lift(i-1)getOrElse "")

Explicación:

(s:String,r:String)=> //define a function
r split "AND"         //split the range expression at every occurance of "AND"
map(                  //map each part...
  _ split "TO"          //split at to
  map(                  //for each of these splitted parts, map them to...
    _.trim.toInt          //trim all whitespace and parse as an int
  )                    
)                     //now we have an Array[Array[Int]]
flatMap{              //map each inner Array...
  case Array(a,b)=>if(a<b)a to b else a to(b,-1) //if it has two elements, create a Range
  case x=>x             //otherwise just return it
}                     //and flatten, so we get an Array[Int]
map(i=>               //for each int
  s lift(i-1)         //try to get the char with index i-1, since Scala is zero-based
  getOrElse ""        //otherwise return ""
) 
corvus_192
fuente
2

Python 2, 156155 bytes

Mi respuesta tiene algunas ideas similares a la respuesta de 1Darco1 , pero al usar un enfoque diferente desde el principio (corte de cadenas en lugar de listas), terminó siendo un poco más corto. Sería cuatro bytes más corto si se permitiera la indexación 0.

s,r=input()
o=""
for x in r.split("AND"):
    i=map(int,x.split("TO"));d=2*(i[0]<i[-1])-1
    for _ in-1,0:i[_]-=11**9*(i[_]<0)
    o+=s[i[0]-1:i[-1]+d-1:d]
print o

Pruébalo en línea

Afortunadamente, puedo analizar cadenas que contienen espacios en enteros. La indexación negativa en los índices de Python desde el final de la cadena, por lo i[-1]que solía ser el mismo i[0]o el segundo valor, si hay uno. Luego tengo que ajustar cualquier valor de rango negativo a más negativo, para que no se enreden con el corte. Multiplicar los valores negativos por 11**9( 2357947691) tendrá en cuenta los rangos utilizando el valor mínimo entero. Luego, simplemente corte la cadena, usando el corte inverso si el rango está invertido.

Con indexación cero (151 bytes):

s,r=input()
o=""
for x in r.split("AND"):
    i=map(int,x.split("TO"));d=2*(i[0]<i[-1])-1
    for _ in-1,0:i[_]-=11**9*(i[_]<0)
    o+=s[i[0]:i[-1]+d:d]
print o
mbomb007
fuente
¡Buen trabajo! Cortar definitivamente es el camino a seguir aquí. Mi rangeenfoque es básicamente una forma súper verbosa de exactamente eso. E incluso te deshiciste de toda la if"T"in x: else:parte. +1
1Darco1
2

R, 142 bytes

Suponiendo que entendí el desafío correctamente, aquí supongo que res la cláusula de rango predefinida en formato de cadena y que la matriz de entrada ("Hola mundo", en los ejemplos) se lee desde stdin.

r=eval(parse(t=paste("c(",gsub("AND",",",gsub("TO",":",r)),")")))
o=strsplit(readline(),e<-"")[[1]][r[r>0]]
o[is.na(o)]=e
c(rep(e,sum(r<1)),o)

Algunos casos de prueba:

r="1 TO 3"
[1] "H" "e" "l"

r="5"
[1] "o"

r="-10 TO 10"
[1] ""  ""  ""  ""  ""  ""  ""  ""  ""  ""  ""  "H" "e" "l" "l" "o" " " "w" "o" "r" "l"

r="0 AND 2 AND 4"
[1] ""  "e" "l"

r="8 TO 3"
[1] "o" "w" " " "o" "l" "l"

r="-300 AND 300"
[1] "" ""

Ungolfed / explicado

Línea 1

r=eval(parse(t=paste("c(",gsub("AND",",",gsub("TO",":",r)),")")))

R tiene un buen operador infijo :que genera secuencias. 1:5da [1, 2, 3, 4, 5]y 0:-2da [0, -1, -2]. Por lo tanto, reemplazamos la TOcláusula de rango suelto con :.

                                         gsub("TO",":",r)

Interpretar ANDes solo concatenación. Podemos usar la función cpara eso, que fácilmente puede tomar un número arbitrario de argumentos, separados por comas. Entonces reemplazamos ANDcon,

                          gsub("AND",",",      ...       )

y luego envolver toda la cosa en c(, ).

               paste("c(",              ...               ,")")

Esto produce una cadena de caracteres que podría verse c( 1 : 5 , 7 ). Llamamos parsea convertir para escribir "expresión" y luego evalevaluar la expresión. La secuencia de números resultante se reasigna a la variable r.

r=eval(parse(t=                     ...                        ))

Línea 2

o=strsplit(readline(),e<-"")[[1]][r[r>0]]

Ahora para la parte fea: lidiar con cadenas en R, que se vuelve desordenado rápidamente. Primero definimos ecomo una cadena vacía (la necesitaremos más adelante).

                      e<-""

Leemos desde stdin y convertimos la cadena de caracteres en una matriz de caracteres individuales dividiéndolos en la cadena vacía. (Por ejemplo, vamos de "Hola" a ["H", "i"].) Esto devuelve una lista de longitud 1, por lo que tenemos que pedir el primer elemento [[1]]para obtener una matriz con la que podamos trabajar. Ugh, te advertí que esto era desordenado.

  strsplit(readline(), ... )[[1]]

R indexa a partir de 1, y tiene una buena característica con números negativos. Supongamos que xes ['a', 'b', 'c']. Llamando x[1]sorprendentemente regresa 'a'. Las llamadas x[-1]devuelven todo x excepto el índice 1, es decir ['b', 'c']. Esta es una característica interesante, pero significa que debemos tener cuidado con nuestros índices negativos para este problema. Entonces, por ahora, solo devolvemos los elementos de la matriz de entrada con índice >0y asignamos el resultado a o.

o=               ...             [r[r>0]]

Línea 3

Sin embargo, hay un problema! Para los índices que son mayores que la longitud de la matriz, R solo devuelve NAvalores. Lo necesitamos para devolver cadenas vacías. Así que volvamos a definir los elementos de opara los que is.na(o)es TRUEser la cadena vacía.

o[is.na(o)]=e

Línea 4

c(rep(e,sum(r<1)),o)

Finalmente, ¿cómo lidiamos con los índices negativos (y cero)? Todos necesitan devolver la cadena vacía, por lo que repetimos la cadena vacía N veces, donde N es el número de índices que hay <1.

  rep(e,sum(r<1))

Finalmente, concatenamos la lista previamente definida oa esta (potencialmente vacía).

c(      ...      ,o)
rturnbull
fuente
2

JavaScript (ES6), 141

Función sin nombre con 2 parámetros, el primero es la matriz de caracteres (puede ser una cadena) y el segundo la cadena que contiene la definición del rango.

El valor de retorno es una matriz donde cada elemento puede ser un solo carácter o el valor js undefined. Cuando se encadena, esto da como resultado una secuencia de caracteres separados por comas que se muestran indefinidos como caracteres "vacíos", como los casos de prueba en la primera versión de la pregunta.
Utilizando .joinpuede obtener un resultado de cadena similar al resultado del caso de prueba en la versión actual de la pregunta.

(l,r,u,z=[])=>(r+' E').replace(/\S+/g,x=>x>'T'?u=t:x>'A'?[...Array((t-(w=(u=u||t)-(d=+u<t?1:-1)-1)-1)*d)].map(_=>z.push(l[w+=d]),u=0):t=x)&&z

Menos golf

(
 l, r, // input paramaters, array/string and string
 u,    // local variable start at 'undefined'
 z=[]  // local variable, will contain the ouput
) => 
  (r+' E') // add an end marker
  .replace( /\S+/g, x=> // execute for each nonspace substring
    x > 'T' // check if 'TO'
    ? u = t // if 'TO' save first value in u (it's a string so even 0 is a truthy value)
    : x > 'A' // check if 'AND' or 'E'
      ? (
          u = u||t, // if u is falsy, it's a single value range t -> t
          d = +u < t ? 1 :-1, // direction of range up or down,comparison has to be numeric, so the leading +
          w = u - d - 1, // starting value (decrement by 1 as js array are 0 based)
          u = 0, // set u to falsy again for next round
          [...Array((t - w - 1) * d)] // build the array of required number of elements
          .map(_ => z.push(l[w+=d])) // iterate adding elements of l to z
        )
      : t = x // if not a keyword, save value in t
  ) && z // return output in z

Prueba

f=
(l,r,u,z=[])=>(r+' E').replace(/\S+/g,x=>x>'T'?u=t:x>'A'?[...Array((t-(w=(u=u||t)-(d=+u<t?1:-1)-1)-1)*d)].map(_=>z.push(l[w+=d]),u=0):t=x)&&z

function run(x)
{
  R.value=x;
  O.textContent=f(L.value,x)
}

run("10 TO 5 AND 5 TO 10")
<table>
<tr><td>Base string</td><td><input id=L value="Hello World"></td></tr>
<tr><td>Custom range</td><td><input id=R ><button onclick='run(R.value)'>-></button></td></tr>
<tr><td>Output</td><td><pre id=O></pre></td></tr>
<tr><td>Test case ranges</td><td>
<select id=T onchange='run(this.value)'>
<option/>  
<option value="1 TO 3">1 TO 3 =&gt; {'H','e','l'}</option>
<option value="5">5 =&gt; {'o'}</option>
<option value="-10 TO 10">-10 TO 10 =&gt; {'','','','','','','','','','','','H','e','l','l','o',' ','W','o','r','l'}</option>
<option value="0 AND 2 AND 4">0 AND 2 AND 4 =&gt; {'','e','l'}
"8 TO 3" => {'o','W',' ','o','l','l'}</option>
<option value="-300 AND 300">-300 AND 300 =&gt; {'',''}</option>
<option value="1 TO 3 AND 3 TO 1">1 TO 3 AND 3 TO 1 =&gt; "HelleH"</option>
<option value="-20 TO 0 AND 1 AND 4">-20 TO 0 AND 1 AND 4 =&gt; "Hl"</option>
</select>
</td></tr>
</table>

edc65
fuente
1

Perl - 110 bytes

Llamar al script en la línea de comando con la cadena como primer argumento y el rango como el segundo.

for(split AND,pop@ARGV){$_>0?print+(split//,"@ARGV")[$_-1]:0for(/(.+)TO(.+)/?($1>$2?reverse$2..$1:$1..$2):$_)}

Desfuscado:

for $subrange (split 'AND', $ARGV[1]) {
    for $index ($subrange =~ /(.+)TO(.+)/
        ? ($1 > $2 ? reverse $2..$1 : $1..$2) # All indices of that range
        : $subrange) # Otherwise, an index only
    {
        if ($index > 0) {
            # Here, 'split' returns an array of all characters
            print((split //, $ARGV[0])[$index - 1]);
        }
    }
}
Maxim Bernard
fuente
1

Python 2, 146 bytes

lambda s,a:[a[i]for x in[map(int,c.split('TO'))for c in s.split('AND')]for i in range(x[0]-1,x[-1]-2*(x[-1]<x[0]),1-2*(x[-1]<x[0]))if 0<=i<len(a)]

Todas las pruebas están en ideone

Divide la cláusula, sen "Y", divide cada una de las subcláusulas resultantes en "TO", convierte las cadenas resultantes para intusar map. Los resultados tendrán cada uno 1 o 2 elementos (1 si no estaba presente "TO" en la subcláusula).
Construye rangos basados ​​en 0 para cada uno de estos utilizando el parámetro de paso de rango como 1 o -1 mediante la inspección de los valores en los índices 0 y -1 (una lista con una entrada tiene esa entrada en ambos índices).
Se ejecuta a través de estos rangos y construye una lista de la salida, si los índices proporcionados están dentro del rango ( if 0<=i<len(a)).

Jonathan Allan
fuente
0

Jalea , 28 27 25 bytes

œṣ⁾TOj”rV
œṣ“Ñþ»Ç€Ff⁹J¤ị⁹

TryItOnline (también funcionará con una cadena en lugar de una matriz de caracteres)

¿Cómo?

œṣ⁾TOj”rV - Link 1, construct sub-range: subclause
  ⁾TO     - string "TO"
œṣ        - split on sublists
     j    - join with
      ”r  - character "r"
        V - evaluate as Jelly code
                (r is the Jelly dyad for inclusive range, which works just like TO
                 when no r is present the string evaluates to the number:
                 " -20 "       evaluates to -20;
                 " -20 r -19 " evaluates to [-20,-19];
                 " 3 r -3 "    evaluates to [3,2,1,0,-1,-2,-3]; etc.)

œṣ“Ñþ»Ç€Ff⁹J¤ị⁹ - Main link: range clause, array 
  “Ñþ»          - compression of the string "AND"
œṣ              - split on sublists
      ǀ        - call the last link (1) as a monad for each
        F       - flatten list
            ¤   - nilad and link(s) as a nilad
          ⁹     - right argument (the array)
           J    - range for length [1,2,...,len(array)]
         f      - filter keep
                      (Jelly indexing is modular so keep only in-bound indexes)
             ị⁹ - index into the array
Jonathan Allan
fuente
0

Clojure 232 230 229 bytes

Oh, qué monstruo he creado ... Pero en realidad esto era 260 cuando estaba a punto de enviarlo.

Editar: eliminó un espacio de #(get r %_""), (if_(< f t)y (take-nth 2_%)(indicado como _).

(let[S clojure.string/split](fn[r s](apply str(map #(get r %"")(mapcat #(apply(fn([f][(dec f)])([f t](if(< f t)(range(dec f)t)(reverse(range(dec t)f)))))%)(map #(mapv read-string(take-nth 2%))(map #(S % #" ")(S s #" AND "))))))))

Menos golfizado:

(def f (let[S clojure.string/split]
         (fn[r s] (->> (map #(S % #" ") (S s #" AND "))
                       (map #(mapv read-string (take-nth 2 %)))
                       (mapcat #(apply(fn
                                        ([f][(dec f)])
                                        ([f t](if (< f t)
                                                (range (dec f) t)
                                                (reverse (range (dec t) f)))))  %))
                       (map #(get r % ""))
                       (apply str)))))

Usos clojure.string/splitpara dividir entre "Y" y "",take-nth coloca "TO" entre enteros, la coincidencia de argumentos de función maneja el caso de 1 o 2 argumentos y eso es todo.

Convención de convocatoria: (f "Hello World" "1 TO 3 AND 2 AND 8 TO 2")

NikoNyrh
fuente
Puede eliminar una gran cantidad de bytes eliminando espacios en general, especialmente entre los #caracteres.
clismique
¿Estás seguro de que podría eliminar un espacio entre cualquiera #? Lo probé sin éxito, se "fusionó" con el token anterior. Oh, un espacio más para eliminar antes de %allí.
NikoNyrh