Recuar uma string usando parênteses


16

Dada a seguinte entrada para o programa:

  1. Lista de caracteres de início do bloco
  2. Lista de caracteres de final de bloco
  3. Uma sequência para formatar

formate a string com os blocos delimitados pelos dois conjuntos de caracteres recuados.

A formatação é feita com dois espaços por nível e os parênteses são colocados como mostrado no exemplo abaixo. Você pode presumir que os conjuntos de caracteres de abertura e fechamento não sejam comuns.

Por exemplo, para {[(<e }])>como os conjuntos de caracteres de abertura e fechamento e a seguinte sequência:

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

a seguinte saída seria esperada:

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

Você não pode codificar a lista de caracteres entre parênteses. Porém, como a entrada é fornecida não é especificado; isso pode ser argumentos da linha de comando ou via entrada padrão, como você deseja.


5
Podemos assumir que, para cada parêntese, há um fechamento e na mesma ordem?
Juan

O programa precisa oferecer suporte a caracteres entre parênteses dados como argumentos? Por exemplo, ./program 'p' 'q' <<< '1p23p45q67q8' ou ele precisa apenas de suporte {[(<e }])>?
Joey Adams

@ Joey, eu diria que não, embora isso seja ainda mais impressionante.
Neil

joey: input são 1. caracteres entre parênteses abertos 2. close chars entre parênteses 3. string para recuar. Juan: podemos assumir que, embora o código não precise confiar nisso, o que quero dizer é que se delim faz parte da abertura de caracteres entre parênteses, aumenta a indentação, caso contrário, se parte dos caracteres de fechamento entre parênteses diminui a indentação.
Prashant Bhate

11
@ Phrasant Bhate: E na saída?
Lowjacker

Respostas:


6

Ruby, 106 101 96 95

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

A entrada é fornecida via linha de comando.


11
Você pode salvar 4 caracteres usando em ~-j+=1vez de (j+=1;j-1). Além disso, o uso de $.qualquer lugar em vez de jpermite remover o arquivo j=0, o que salva outro caractere.
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 deveria haver espaço para eliminar expressões comuns, pelo menos, mas é uma abordagem rápida que lida com o exemplo, bem como a hipótese de Joey Adams sobre colchetes arbitrários.


De fato, havia muito espaço para melhorias:

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

... e ainda um pouco mais:

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

3

Mathematica (golfe sem 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
]

Teste

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

        ]

    }

}

Como bônus, a função a seguir pode ser usada para formatar a expressão mathematica

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

EDIT (golfe não codificado) Atualizado com controle granular fino sobre a maneira como as novas linhas são renderizadas

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]]

Resultado

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 ]
                 ]
             ]
         ]

Isso dificilmente é código de golfe, com nomes de vários caracteres como indent. O seu objetivo é um código maximamente conciso ou legível? Existem várias maneiras de reduzir esse código, se esse é realmente o seu objetivo. Além disso: "Você não pode codificar a lista de caracteres" parênteses "." ainda não foi exatamente o que você fez aqui? Enfim, desculpe soar tão negativo; isso me parece uma resposta estranha ao seu próprio desafio.
Mr.Wizard

11
@ Mr.Wizard não é código de golfe, eu o adicionei para minha própria referência [atualizado para deixar claro]. Eu freqüentemente usá-lo para entender o código mathematica não formatado que abrangem maior do que uma página
Prashant Bhate

3

JavaScript, 255 227 205 caracteres

Ei, seu comprimento se encaixa perfeitamente em um byte! : D

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}

É uma função, passe os caracteres iniciais, finais e depois o texto.


Seu próprio comentário de edição foi usado contra você. : D
Maçaneta da porta

@ Doorknob: Eu ... pensei que nunca tinha feito isso. D: Sinto muito. (Você estava caçando?)
Ry-

@ Doorknob: E obrigado por me lembrar sobre isso; abreviado :)
Ry-

Não, eu não estava caçando, apenas me deparei com essa pergunta, mas decidi e encontrei o seguinte : O: P
Maçaneta da porta

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

Observe que a tarefa solicita que os dois conjuntos de parênteses façam parte da entrada, não codificados.
Joey

@ Joey observou, eu vou resolver isso daqui a pouco. Obrigado
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
)

Você precisa dos espaços após as printdeclarações?
Zacharý

2

C - 213 209

Eu odeio erros 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);}}

Lê parens à esquerda do primeiro argumento da linha de comando, parêntes à direita do segundo argumento e entrada para recuar no stdin.

Muito impresso e 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);}

Custou-me 66 caracteres extras apenas para corrigir o erro com as linhas vazias :( Francamente, preciso de uma nova abordagem, mas vou encerrar o dia por enquanto.

#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);}}

Uma abordagem bastante rápida e suja. Ele tem um problema de produzir linhas vazias entre parênteses de fechamento consecutivos, mas, caso contrário, ele faz o trabalho (ou pelo menos eu acho). Vou revisitá-lo para uma solução melhor e mais limpa, ainda esta semana.

char * b é o conjunto de parênteses de abertura, char * e é o conjunto de parênteses de fechamento e char * s é a sequência de entrada.


1

Perl - 69 bytes

TMTOWTDI simplifica o código

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

3
Você deve pegar os parênteses como entrada, não codificá-los.
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

Uma versão da resposta de Hojung Youn que aceita os caracteres do bloco por meio de dois argumentos.

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

Chamado como:

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

Conceito muito bom, @Hojung e Sorpigal. É um pouco frágil, no entanto. Por exemplo, troque as teclas] e} no argumento de fechar parênteses, e o] fecha a classe de caracteres, levando a um erro de parêntese inigualável. Da mesma forma, suponha que o conjunto aberto comece com ^, talvez para corresponder a v no conjunto próximo; você receberá o complemento da classe [$ a] pretendida. Foi por isso que usei \ Q ... \ E na minha resposta. O \ w + para caracteres que não são parêntesis funciona por exemplo, mas e quanto à entrada como 'x (foo-bar) y' '(' ')'? Obviamente, não está claro que o código precisa lidar com algo assim.
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='')

Exemplo:

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

heinrich5991 sugeriu salvar dois personagens, alterando a segunda linha para_,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}

Você pode salvar o script em um arquivo indent.groovy e experimentá-lo com:
groovy indent.groovy "abc {xyz {text [note {comment (t {ex} t) abc}]}}" "{[(" ") ]} "


Eu experimentei no groovy por uma hora antes de ver sua resposta, usei uma abordagem semelhante, mas a minha é muito mais longa que a sua, então nem me preocupo em postar .. Bom trabalho! :)
Fels

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

Uma versão não destruída do 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

Os argumentos para o programa são (em ordem): os parênteses de abertura, os parênteses de fechamento e o texto a ser recuado.

Exemplo ($ é prompt de linha 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);}

precisa de uma sequência terminada nula para a verificação de limites (caso contrário, if(*s)precisa ser alterada para if(s.length))


Observe que a tarefa solicita que os dois conjuntos de parênteses façam parte da entrada, não codificados.
Joey

0

Java

Non codegolf version! Supondo que tenhamos esta versão de split () que inclui delins,

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
StringUtilsnão faz parte do JDK padrão.
st0le 7/07/11

0

C 284 Caracteres não em branco

Eu não sou fã de ofuscação, mas bem ...

#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;}}

A função pega string, abrindo delimitadores, terminando delimitadores como argumentos.


0

C, 256

Parâmetros:

  • e é o caractere final,
  • n é o recuo,
  • b os suportes de abertura,
  • d os suportes de fechamento.

Eu quebrei o código para evitar a barra de rolagem 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++)}}}

O programa completo tem 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)

O idioma não é adequado para codificar o golfe, então usei uma abordagem incomum. Usando um ouvinte de rastreamento para gerar saída para o console.

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

Usa argumentos da linha de comando para a 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}

Explicação Ungolfed

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)}}

Praticamente a abordagem mais direta que se possa imaginar. Itere pela string (v [3]), se for uma chave esquerda (conforme definido em v [1]), aumente o nível de indentação, se for uma chave direita (como definido em v [2]), diminua o nível de indentação .


-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);}

Não é muito bom, mas uma solução .. uma linha vazia pode aparecer antes / depois, dependendo se a entrada começa ou termina entre parênteses.

Com a nova restrição, essa abordagem é quase inútil para o golfe.


Não recua os parênteses de abertura o suficiente e gera linhas vazias entre as que se fecham consecutivamente.
Joey

@ joey fixo, obrigado pelo feedback!
esneider

Ele ainda codifica os parênteses enquanto eles devem fazer parte da entrada. Atualmente, todas as respostas não estão em conformidade com a especificação.
Joey
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.