Sangrar una cadena usando paréntesis dados


16

Dada la siguiente entrada al programa:

  1. Lista de caracteres de inicio de bloque
  2. Lista de caracteres finales de bloque
  3. Una cadena para formatear

formatee la cadena con los bloques delimitados por los dos juegos de caracteres sangrados.

El formateo se realiza con dos espacios por nivel y los paréntesis se colocan como se muestra en el siguiente ejemplo. Puede suponer que los conjuntos de caracteres de apertura y cierre son disjuntos.

Por ejemplo, para {[(<y }])>como los conjuntos de caracteres de apertura y cierre y la siguiente cadena:

abc{xyz{text[note{comment(t{ex}t)abc}]}}

se esperaría el siguiente resultado:

abc
{
  xyz
  {
    text
    [
      note
      {
        comment
        (
          t
          {
            ex
          }
          t
        )
        abc
      }
    ]
  }
}

No puede codificar la lista de caracteres de "paréntesis". Sin embargo, no se especifica cómo se proporciona la entrada; esto podría ser argumentos de línea de comandos o mediante entrada estándar, como lo desee.


55
¿Podemos suponer que para cada paréntesis hay uno de cierre y en el mismo orden?
Juan

¿El programa tiene que soportar algún paréntesis dado como argumento? por ejemplo, ¿ ./program 'p' 'q' <<< '1p23p45q67q8' o solo necesita soporte {[(<y }])>?
Joey Adams

@Joey, supongo que no, aunque eso sería aún más impresionante.
Neil

joey: los datos de entrada son 1. caracteres de paréntesis abiertos 2.cerrar caracteres de paréntesis 3. cadena a sangría. Juan: podemos suponer que, aunque el código no necesita depender de eso, lo que quiero decir es que si la delimitación es parte de los caracteres de paréntesis de apertura, aumenta la sangría, de lo contrario si parte de los caracteres de paréntesis de cierre disminuye la sangría.
Prashant Bhate

1
@Phrasant Bhate: ¿Y en la salida?
Lowjacker

Respuestas:


6

Rubí, 106 101 96 95

s,e,i=$*
i.scan(/[#{z=Regexp.quote s+e}]|[^#{z}]*/){|l|puts'  '*(s[l]?~-$.+=1:e[l]?$.-=1:$.)+l}

La entrada se proporciona a través de la línea de comando.


1
Puede guardar 4 caracteres usando en ~-j+=1lugar de (j+=1;j-1). Además, el uso en $.todas partes en lugar de le jpermite eliminar el j=0, lo que guarda otro personaje.
Ventero

6

Perl - 131 96 94 caracteres

$i="";for$_(split/([\Q$ARGV[0]$ARGV[1]\E])/,$ARGV[2]){$i=~s/..// if/[\Q$ARGV[1]\E]/;print "$i$_\n"if$_;$i.='  'if/[\Q$ARGV[0]\E]/;}

Parece que debería haber espacio para eliminar expresiones comunes, al menos, pero es una toma rápida que maneja el ejemplo, así como la hipótesis hipotética de Joey Adams sobre paréntesis arbitrarios.


De hecho, había mucho margen de mejora:

$_=pop;($s,$e)=map"[\Q$_\E]",@ARGV;for(split/($s|$e)/){print"  "x($i-=/$e/),"$_\n"if$_;$i+=/$s/}

... y aún un poco más:

$_=pop;($s,$e)=map"[\Q$_\E]",@ARGV;map{print"  "x($i-=/$e/),"$_\n"if$_;$i+=/$s/}split/($s|$e)/

3

Mathematica (golf sin código)

indent[str_String]:=Module[{ind,indent,f},
ind=0;
indent[i_]:="\n"<>Nest["    "<>ToString[#]&,"",i];
f[c_] :=  (indent[ind] <> c <> indent[++ind]) /; StringMatchQ["[({",___~~c~~___];
f[c_] := ( indent[--ind] <> c <>indent[ind])  /; StringMatchQ["])}",___~~c~~___];
f[c_] := (c <>indent[ind])       /; StringMatchQ[";,",___~~c~~___];
f[c_] := c  ;
f /@ Characters@ str//StringJoin
]

Prueba

indent["abc{xyz{text[note{comment(t{ex}t)abc}]}}"]
abc
{
    xyz
    {
        text
        [
            note
            {
                comment
                (
                    t
                    {
                        ex
                    }
                    t
                )
                abc
            }

        ]

    }

}

Como beneficio adicional, la siguiente función se puede utilizar para formatear la expresión matemática

format[expr_] := indent[expr // FullForm // ToString]

EDITAR (sin código de golf) Actualizado con control granular fino sobre la forma en que se representan las nuevas líneas

indent[str_String, ob_String, cb_String, delim_String] := 
  Module[{ind, indent, f, tab}, ind = 0; tab = "    ";
   indent[i_, tab_, nl_] := nl <> Nest[tab <> ToString[#] &, "", i];
   f[c_] := (indent[ind, "", " "] <> c <> indent[++ind, tab, "\n"]) /;StringMatchQ[ob, ___ ~~ c ~~ ___];
   f[c_] := (indent[--ind, "", " "] <> c <> indent[ind, tab, "\n"]) /;StringMatchQ[cb, ___ ~~ c ~~ ___];
   f[c_] := (c <> indent[ind, tab, "\n"]) /;StringMatchQ[delim, ___ ~~ c ~~ ___];
   f[c_] := c;
   f /@ Characters@str // StringJoin];
format[expr_] := indent[expr // InputForm // ToString, "[({", "])}", ";"];

format[Hold@Module[{ind, indent, f, tab}, ind = 0; tab = "    ";
 indent[i_, tab_, nl_] := nl <> Nest[tab <> ToString[#] &, "", i];
 f[c_] := (indent[ind, "", " "] <> c <> indent[++ind, tab, "\n"]) /;StringMatchQ[ob, ___ ~~ c ~~ ___];
 f[c_] := (indent[--ind, "", " "] <> c <> indent[ind, tab, "\n"]) /;StringMatchQ[cb, ___ ~~ c ~~ ___];
 f[c_] := (c <> indent[ind, tab, "\n"]) /;StringMatchQ[delim, ___ ~~ c ~~ ___];
 f[c_] := c;
 f /@ Characters@str // StringJoin]]

Salida

Hold [
    Module [
         {
            ind, indent, f, tab }
        , ind = 0;
         tab = "    ";
         indent [
            i_, tab_, nl_ ]
         := StringJoin [
            nl, Nest [
                StringJoin [
                    tab, ToString [
                        #1 ]
                     ]
                 & , "", i ]
             ]
        ;
         f [
            c_ ]
         := StringJoin [
            indent [
                ind, "", " " ]
            , c, indent [
                ++ind, tab, "\n" ]
             ]
         /;
         StringMatchQ [
            ob, ___~~c~~___ ]
        ;
         f [
            c_ ]
         := StringJoin [
            indent [
                --ind, "", " " ]
            , c, indent [
                ind, tab, "\n" ]
             ]
         /;
         StringMatchQ [
            cb, ___~~c~~___ ]
        ;
         f [
            c_ ]
         := StringJoin [
            c, indent [
                ind, tab, "\n" ]
             ]
         /;
         StringMatchQ [
            delim, ___~~c~~___ ]
        ;
         f [
            c_ ]
         := c;
         StringJoin [
            f / @
                 Characters [
                    str ]
                 ]
             ]
         ]

Eso es apenas un código de golf, con nombres de varios caracteres como indent. ¿Es su objetivo el código máximo o la legibilidad? Hay varias formas de acortar ese código, si ese es su objetivo. Además: "No puede codificar la lista de caracteres de" paréntesis ". ¿pero no es eso exactamente lo que hiciste aquí? De todos modos, lamento sonar tan negativo; Simplemente me parece una respuesta extraña a tu propio desafío.
Mr.Wizard

1
@ Mr.Wizard no es código de golf, lo he agregado para mi propia referencia [actualizado para que quede claro]. Con frecuencia lo uso para entender el código Mathematica sin formato que se extienden más grande que una página
Prashant Bhate

3

JavaScript, 255 227 205 caracteres

¡Hey, su longitud cabe perfectamente en un byte! :RE

function(s,e,t){R=eval.bind(0,"Array(n).join(' ')");for(i=n=0,b=r='';c=t[i++];)~s.indexOf(c)?(r+=b,b='\n'+R(++n)+c+'\n '+R(++n)):~e.indexOf(c)?b+='\n'+((n-=2)?R()+' ':'')+c+'\n'+(n?R()+' ':''):b+=c;return r+b}

Es una función, pásala por los caracteres iniciales, los caracteres finales y luego el texto.


Su propio comentario de edición se ha utilizado en su contra. : D
Pomo de la puerta

@Doorknob: Yo ... pensé que nunca había hecho eso. D: Lo siento mucho. (¿Estabas cazando?)
Ry-

@Doorknob: Y gracias por recordarme esto; acortado :)
Ry-

No, no estaba cazando, simplemente me topé con esta pregunta, pero decidí hacerlo y encontré esto : O: P
Pomo de la puerta

2

Python - 162 caracteres

i=f=0
s=""
l,r,z=[raw_input()for c in'   ']
o=lambda:s+("\n"+"  "*i)*f+c
for c in z:
 if c in l:f=1;s=o();i+=1
 elif c in r:i-=1;f=1;s=o()
 else:s=o();f=0
print s

Tenga en cuenta que la tarea requiere que los dos conjuntos de paréntesis sean parte de la entrada, no codificados.
Joey

@Joey notó, voy a arreglar eso en un tiempo. Gracias
Juan

2

Python 2.7.X - 136 caracteres

import sys
a,c=sys.argv,0
for i in a[3]:
 if not(i in a[2]):print ' '*c+i
 else:print ' '*(c-4)+i
 if i in a[1]:c+=4
 if i in a[2]:c-=4

Uso : $ ./foo.py '(' ')' '(ab (cd (ef) gh) ij)'

Resultado resultante:

(
    a
    b
    (
        c
        d
        (
            e
            f
        )
        g
        h
    )
    i
    j
)

¿Necesita los espacios después de las printdeclaraciones?
Zacharý

2

C - 213 209

Odio los errores estúpidos ...>. <

#include<stdio.h>
#include<string.h>
int main(int i,char**s){for(char q,r,c,t,a=0;~(c=getchar());t=q|r){q=!!strchr(s[1],c);a-=r=!!strchr(s[2],c);for(i=0;t|q|r&&i<2*a+1;putchar(i++?' ':'\n'));a+=q;putchar(c);}}

Lee los parentes izquierdos del primer argumento de la línea de comandos, los parentescos derechos del segundo argumento y la entrada para sangrar en stdin.

Impreso y comentado:

int main(int i, char **s) {
  for (char q, r, /* is left-paren? is right-paren? */
            c,    /* character read from input */
            t,    /* last char was a paren-char */
            a=0;  /* indentation */
       ~(c = getchar());
       t = q|r) {
         q = !!strchr(s[1],c);
    a -= r = !!strchr(s[2],c);
    for (i=0; t|q|r && i<2*a+1; putchar(i++? ' ' : '\n'));
    a += q;
    putchar(c);
  }
}

1

C ( 159 225 caracteres)

#define q(s,c)strchr(s,c)
#define p(i,j,k)printf("\n%*s%c%c%*s",i,"",*s,k,j,"")
g(char*b,char*e,char*s){int i;for(i=0;*s;s++)q(b,*s)?p(i-2,i+=2,'\n'):q(e,*s)?q(b,*(s+1))||q(e,*(s+1))?p(i-=2,i-2,0):p(i-=2,i-2,'\n'):putchar(*s);}

Me costó 66 caracteres adicionales solo para corregir el error con las líneas vacías :( Francamente, necesito un nuevo enfoque, pero lo llamaré un día por ahora.

#define p(i,j)printf("\n%*s%c\n%*s",i,"",*s,j,"")
f(char*b,char*e,char*s){int i;for(i=0;*s;s++){strchr(b,*s)?p(i-2,i+=2):strchr(e,*s)?p(i-=2,i-2):putchar(*s);}}

Un enfoque bastante rápido y sucio. Tiene un error de producir líneas vacías entre paréntesis de cierre consecutivos, pero de lo contrario hace el trabajo (o eso creo). Lo revisaré para una solución mejor y más limpia, en algún momento de esta semana.

char * b es el conjunto de paréntesis de apertura, char * e es el conjunto de paréntesis de cierre y char * s es la cadena de entrada.


1

Perl - 69 bytes

TMTOWTDI simplifica el código

#!perl -p
s/([[{(<])|([]})>])|\w+/"  "x($1?$t++:$2?--$t:$t)."$&
"/ge

3
Se supone que debes tomar los paréntesis como entrada, no codificarlos.
Gareth

1

Scala (2.9), 211 caracteres

object P extends App{def x(j:Int)={"\n"+"  "*j}
var(i,n)=(0,"")
for(c<-args(2)){if(args(0).exists(_==c)){print(x(i)+c)
i+=1
n=x(i)}else{if(args(1).exists(_==c)){i-=1
print(x(i)+c)
n=x(i)}else{print(n+c)
n=""}}}}

1

Perl - 89 85 bytes

Una versión de la respuesta de Hojung Youn que acepta los caracteres de bloque a través de dos argumentos.

#!perl -p
BEGIN{$b=pop;$a=pop}s/([$a])|([$b])|\w+/"  "x($1?$t++:$2?--$t:$t)."$&
"/ge

Llamado como:

perl golf.pl<<<'abc{xyz{text[note{comment(t{ex}t)abc}]}}' '[{(<' ']})>'

Muy buen concepto, @Hojung y Sorpigal. Sin embargo, es un poco frágil. Por ejemplo, cambie el] y} en el argumento close-paren, y el] cierra la clase de caracteres, lo que lleva a un error de paren sin igual. Del mismo modo, suponga que el conjunto abierto comienza con ^, quizás para que coincida con v en el conjunto cerrado; obtendrá el complemento de la clase [$ a] prevista. Es por eso que usé \ Q ... \ E en mi respuesta. El \ w + para caracteres no pareados funciona para el ejemplo, pero ¿qué pasa con la entrada como 'x (foo-bar) y' '(' ')'? Por supuesto, no está claro si el código necesita manejar algo así.
DCharness

1

Python3, 184 182 caracteres

import sys
_,p,q,t=sys.argv
i,f,x=0,1,print
for e in t:
 if e in p:f or x();x(' '*i+e);i+=2;f=1
 elif e in q:f or x();i-=2;f=1;x(' '*i+e)
 else:not f or x(' '*i,end='');f=x(e,end='')

Ejemplo:

$ python3 ./a.py '{[(<' '}])>' 'abc{xyz{text[note{comment(t{ex}t)abc}]}}'
abc
{
  xyz
  {
    text
    [
      note
      {
        comment
        (
          t
          {
            ex
          }
          t
        )
        abc
      }
    ]
  }
}

heinrich5991 sugirió guardar dos caracteres cambiando la segunda línea a_,p,q,t=sys.argv
Peter Taylor

1

Groovy, 125

p=args;i=0;s={a,b->"\n"+"\t"*(b?i++:--i)+a+"\n"+"\t"*i};p[0].each{c->print p[1].contains(c)?s(c,1):p[2].contains(c)?s(c,0):c}

Puede guardar el script en un archivo indent.groovy y probarlo con:
groovy indent.groovy "abc {xyz {text [note {comment (t {ex} t) abc}]}}}" "{[(" ") ]} "


Lo intenté durante una hora antes de ver tu respuesta, utilicé un enfoque similar, pero el mío es mucho más largo que el tuyo, así que ni siquiera me molestaré en publicar ... ¡Buen trabajo! :)
siente el

1

Python - 407

from sys import*;o=argv[1];c=argv[2];t=argv[3];p=0;n=False;a=lambda:h not in e;b=lambda s:print(s+(" "*p)+h);r="";e=o+c
for h in t:
 for k in o:
  if h==k:
   if(r in e)and(r!=""):b("")
   else:b("\n")
   p+=2;n=True;break
 for k in c:
  if h==k:
   p-=2
   if(r in e)and(r!=""):b("")
   else:b("\n")
   n=True;break
 if a()and n:print((" "*p)+h,end="");n=False
 elif a():print(h,end="")
 r=h

Una versión no adaptada del programa:

import sys

open_set = sys.argv[1]
close_set = sys.argv[2]
text = sys.argv[3]
spaces = 0
newline = False
a = lambda : char not in b_set
b = lambda s: print(s + (" " * spaces) + char)
prev = ""
b_set = open_set + close_set

for char in text:
    for bracket in open_set:
        if char == bracket:
            if (prev in b_set) and (prev != ""):
                b("")
            else:
            b("\n")
        spaces += 2
        newline = True
        break
    for bracket in close_set:
        if char == bracket:
            spaces -= 2
            if (prev in b_set) and (prev != ""):
                b("")
            else:
                b("\n")
            newline = True
            break
    if a() and newline:
        print((" " * spaces) + char, end="")
        newline = False
    elif a():
        print(char, end="")
    prev = char

Los argumentos para el programa son (en orden): los paréntesis de apertura, los paréntesis de cierre y el texto para sangrar.

Ejemplo ($ es la línea de comando):

$ python indent.py "{[(<" "}])>" "abc{xyz{text[note{comment(t{ex}t)abc}]}}"
abc
{
  xyz
  {
    text
    [
      note
      {
        comment
        (
          t
          {
            ex
          }
          t
        )
        abc
      }
    ]
  }
}

0

D (300)

C[] i(C,S)(ref S s,C p){if(!*s)return[];static C[] w;w~=" ";C[] r;C c=s[0];while(c!=p){s=s[1..$];r~=(c=='{'||c=='['||c=='<'?"\n"~w~c~"\n"~i(s,cast(char)(c+2)):c=='('?"\n"~w~c~"\n"~i(s,')'):[c]);c=*s;}w=w[1..$];if(*s)s=s[1..$];c=*s;return" "~w~r~"\n"~w~(c=='}'||c==']'||c=='>'||c==')'?[p]:p~"\n"~w);}

necesita una cadena terminada en nulo para la verificación de límites (de lo contrario if(*s), debe cambiarse a if(s.length))


Tenga en cuenta que la tarea requiere que los dos conjuntos de paréntesis sean parte de la entrada, no codificados.
Joey

0

Java

No codegolf version! Suponiendo que tenemos esta versión de split () que incluye delims,

public static String indent(String input, String openPars,
        String closingPars) {
    String re = "["
            + (openPars + closingPars).replace("[", "\\[").replace("]",
                    "\\]") + "]";
    String[] split = inclusiveSplit(input, re, 0);
    int indent = 0;
    StringBuilder sb = new StringBuilder();
    for (String string : split) {
        if (StringUtils.isEmpty(string))
            continue;
        if (closingPars.indexOf(string) != -1) {
            indent--;
        }
        sb.append(StringUtils.repeat(" ", indent * 2));
                    sb.append(string);
                    sb.append("\n");
        if (openPars.indexOf(string) != -1) {
            indent++;
        }
    }
    String string = sb.toString();
    return string;
}

2
StringUtilsno es parte del JDK estándar.
st0le

0

C 284 Caracteres no blancos

No soy fanático de la ofuscación, pero bueno ...

#include<cstdio>
#include<cstring>
#define g printf
#define j char
int main(int a,j**b){int c=0;for(j*f=b[3];*f!='\0';++f){if(strchr(b[1],*f)!=0){g("\n%*c\n%*c",c,*f,c+2,'\0');c+=2;}else if(strchr(b[2],*(f))!=0){c-=2;g("\n%*c",c,*f);if(strchr(b[2],*(f+1))==0)g("\n%*c",c,'\0');}else putchar(*f);}}

Uso: ./program start_brackets end_brackets string_to_parse


0

php (187) (153)

function a($s,$o,$e){while(''!=$c=$s[$i++]){$a=strpbrk($c,$o)?2:0;$b=strpbrk($c,$e)?2:0;echo ($a+$b||$r)?"\n".str_pad('',$t-=$b):'',$c;$t+=$a;$r=$a+$b;}}

La función toma cadenas, delimitadores de apertura, delimitadores finales como argumentos.


0

C, 256

Parámetros:

  • e es el char final,
  • n es la sangría,
  • b los corchetes de apertura,
  • d los corchetes de cierre.

Rompí el código para evitar la barra de desplazamiento horizontal.

#define r char
#define P(c) putchar(c);
#define N P(x)
#define W printf("%*s",n,"");
r*s,x='\n';i(r e,int n,r*b,r*d){r*t=s,*p;int l=0;W while(*s!=e)    
{if(p=strchr(b,*s)){if(s!=t){N W}P(*s++)N i(d[p-b],n+2,b,d); N W 
P(*s++);l=1;}else{if(l){N W l=0;}P(*s++)}}}

El programa completo tiene 363 caracteres.

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define r char
#define P(c) putchar(c);
#define N P(x)
#define W printf("%*s",n,"");
r*s,x='\n';i(r e,int n,r*b,r*d)
{r*t=s,*p;int l=0;W while(*s!=e)
{if(p=strchr(b,*s)){if(s!=t){N W}
P(*s++)N i(d[p-b],n+2,b,d); N W
P(*s++);l=1;}else{if(l){N W l=0;}
P(*s++)}}}main(int c,r*v[]){s =
v[3];i('\0',0,v[1],v[2]);}

0

VB.net (? C)

El lenguaje no es adecuado para el golf de código, así que utilicé un enfoque poco común. Usar un escucha de rastreo para enviar a la consola.

Imports System.Diagnostics.Debug
Module Module1
  Sub Main(args() As String)
    IndentText(args(0), args(1), args(2)) 'openings, closings, text)
  End Sub
  Sub IndentText(o As String, e As String, t As String)
    Dim x = 0
    Listeners.Add(New Diagnostics.ConsoleTraceListener)
    IndentSize = 2
    For Each c In t
      If o.Contains(c) Then
        WriteLine("")
        WriteLine(c)
        Indent()
        x = 1
      ElseIf e.Contains(c) Then
        If x = 0 Then WriteLine("")
        Unindent()
        WriteLine(c)
        x = 1
      Else
        Write(c)
        x = 0
      End If
    Next
  End Sub
End Module

Utiliza argumentos de línea de comando para la entrada

args(0) is the indenting chars
args(1) is the undenting chars
args(2) is the text to be indented.

0

Powershell, 146 bytes

param([char[]]$s,[char[]]$e,[char[]]$f)$f|%{}{if($_-in$s){$o;'  '*$i+$_;$o='  '*++$i;}elseif($_-in$e){$o;'  '*--$i+$_;$o='  '*$i}else{$o+=$_}}{$o}

Explicación sin golf

param([char[]]$start,             # Cast as array of Chars
      [char[]]$end,
      [char[]]$string)
$string | foreach-object { } {    # For every char in string. Empty Begin block
    if ( $_ -in $start ) {        # If char is in start
        $o                        # Print stack ($o)
        '  ' * $i + $_            # Newline, indent, insert start char
        $o = '  ' * ++$i          # Set stack to ident (incremented)
    } elseif ( $_ -in $end ) {    # If char is in end
        $o                        # Print stack
        '  ' * --$i + $_          # Newline, decrement indent, insert end char
        $o = '  ' * $i            # Set stack to indent
    } else {
        $o+ = $_                  # Otherwise add character to stack
    }
} { $o }                          # Print remaining stack (if any)

0

C, 181 caracteres

#define d(m,f)if(strchr(v[m],*s)){puts("");for(j=f;j--;)printf("  ");}
i;main(j,v,s)char**v,*s;{for(s=v[3];*s;s++){d(1,i++)d(2,--i)putchar(*s);d(1,i)if(!strchr(v[2],*(s+1)))d(2,i)}}

Prácticamente el enfoque más directo imaginable. Itere a través de la cadena (v [3]), si es una llave izquierda (como se define en v [1]), aumente el nivel de sangría, si es una llave derecha (como se define en v [2]), disminuya el nivel de sangría .


-1

C, 114 121

main(i,x,s,c){while(~(c=getchar()))(s=x)|(x=2*!!strchr("(){}[]<>",c))?s=c-1&x,i-=x-2*s,printf("\n%*c",i-s,c):putchar(c);}

No es muy agradable, pero una solución ... puede aparecer una línea vacía antes / después dependiendo de si la entrada comienza / termina con paréntesis.

Con la nueva restricción, este enfoque es casi inútil para el golf.


No sangra los paréntesis de apertura lo suficiente y genera líneas vacías entre las de cierre consecutivas.
Joey

@joey arreglado, gracias por los comentarios!
esneider

Todavía codifica los paréntesis mientras deben ser parte de la entrada. Actualmente, todas las respuestas no se ajustan a la especificación.
Joey
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.