Vamos fazer Diet Haskell

21

Haskell possui tuplas que podem ser escritas como

(a,b,c)

No entanto, este é apenas açúcar sintático para

(,,)a b c

Em geral, uma tupla n pode ser formada com n-1 , s entre (... )seguido por seus elementos separados por espaços. Por exemplo, a 7-tupla, (1,2,3,4,5,6,7)pode ser formada por

(,,,,,,)1 2 3 4 5 6 7

Como Haskell não possui tuplas 1, elas não podem ser formadas. Você também não será responsável por tuplas vazias.

Tuplas aninhadas podem ser formadas usando parênteses para substituir a ordem das operações.

((1,2),3) == (,)((,)1 2)3

Como parte de nossa busca para remover todo o açúcar sintático do Haskell , vou pedir que você escreva um programa que remova o açúcar sintático das tuplas de Haskell também.

Seu programa deve usar uma tupla, uma matriz ou uma string representando uma tupla açucarada e deve gerar uma string representando uma tupla "sem açúcar". As tuplas de entrada sempre conterão números inteiros positivos ou outras tuplas.

Como estamos jogando golfe aqui, sua produção deve ser curta. Não deve conter desnecessários

  • Espaços. Os espaços devem ser usados ​​apenas para separar argumentos de uma função de tupla e não devem aparecer após um )ou antes de um(

  • Parênteses. Parênteses devem ser usados ​​apenas ao formar funções de tupla ou ao aninhar tuplas.

Esta é uma questão de para que as respostas sejam pontuadas em bytes, com menos bytes sendo melhores.

Casos de teste

(1,2)     -> (,)1 2
(1,2,3)   -> (,,)1 2 3
((1,2),3) -> (,)((,)1 2)3
(1,2,3,4) -> (,,,)1 2 3 4
(1,(2,3)) -> (,)1((,)2 3)
(10,1)    -> (,)10 1
Assistente de Trigo
fonte
Se não estou perdendo nada, você cobre tuplas de 1, mas não as vazias ..? As tuplas vazias são válidas?
totallyhuman 4/17/17
3
@totallyhuman Você não precisa lidar com tuplas vazias.
Assistente de trigo
O quinto caso de teste tem um extra,
H.PWiz 4/17/17
2
Também por "números" você quer dizer "números inteiros positivos"?
Erik the Outgolfer
2
Casos de teste sugeridos: ((1,(2,3)),4,(5,6))e (1,(2,3),4).
Ørjan Johansen

Respostas:

17

Haskell , 169 148 bytes

init.tail.fst.([]%)
p:k="(,"
l%('(':r)|(y,x:s)<-[]%r,m<-y:l=last$m%(p:s):[(p:p:(l>>k)++x:foldl(\r x->x++[' '|x>k,r>k]++r)[x]m,s)|x<',']
l%r=lex r!!0

Experimente online! Toma a tupla como uma string. init.tail.fst.([]%)é a função principal anônima. Ligue-o a, por exemplo, fe use como f "(3,(14,1),4,7)", o que produz "(,,,)3((,)14 1)4 7".

Por que a entrada não é fornecida como uma tupla de Haskell, você pergunta? Como Haskell é fortemente digitado, uma tupla (1,2)tem o tipo (Int,Int)1 e uma tupla (1,(2,3))tem o tipo (Int,(Int,Int)). Assim, uma função que aceita o primeiro tipo de tupla não pode ser aplicada ao segundo tipo e, especialmente, não pode haver função que use uma tupla arbitrária 2 .

Explicação:

  • p:k="(,"é uma maneira curta de atribuir pa '('e kpara ",".
  • (%)é a função de análise e conversão recursiva. O primeiro argumento é uma lista de entradas de tupla já analisadas, o segundo argumento é o restante da string original. Cada chamada retorna uma tupla da tupla atual convertida (como uma sequência e entre colchetes) e o restante da sequência.
    • l%('(':r)Se a sequência começar com um colchete de abertura, precisamos analisar uma nova entrada da tupla.
      (y,x:s)<-[]%rAplicamos recursivamente %e obtemos uma entrada de tupla ye a sequência restante dividida no próximo caractere xe no restante da sequência s.
      m<-y:lAdicionamos a nova entrada yà lista atual de entradas já encontradas le chamamos o resultado m.
    • O próximo caractere xagora é uma vírgula ,ou um colchete de fechamento ). O last$ <B> :[ <A> |x<',']é apenas uma maneira mais curta da escrita if x == ')' then <A> else <B>.
    • Portanto, se a ,for o próximo, precisamos analisar recursivamente a próxima entrada: m%(p:s)Anexamos um colchete de abertura para terminar no caso correto e passar a lista de entradas já encontradas m.
    • Caso contrário x == ')', concluímos a tupla atual e precisamos fazer a transformação necessária:(p:p:(l>>k)++x:foldl(\r x->x++[' '|x>k,r>k]++r)[x]m,s)
      • p:p:(l>>k)++x:Se temos achado n entradas, então mtem n elementos e y, a lista antes de adicionar o elemento mais encontrado recentemente, tem n-1 entradas. Isso é útil, pois precisamos do n-1 , para uma ntupla de elemento e l>>ktrabalha em listas como "concatenar a lista kconsigo mesma quantas vezes ytiver elementos" . Assim, esta primeira parte produz algumas cordas como "((,,,)".
      • foldl(\r x->x++[' '|x>k,r>k]++r)[x]mconcatena os elementos de m(na ordem inversa, porque adicionando novas entradas à frente em msi foi construído na ordem inversa) e adicionando apenas espaços entre dois elementos se ambos forem números: [' '|x>k,r>k]verificamos se as entradas atuais xe ros números são comparados lexicograficamente eles para ","- se eles não são números, eles já são uma representação de tupla entre colchetes e são '(' < ','retidos.
    • Se o padrão de jogo l%('(':r)logo no início falhar, então vamos acabar na última linha: l%r=lex r!!0. Isso significa que precisamos analisar um número e retornar o número e o restante da string. Felizmente, existe a lexfunção que faz exatamente isso (analisa o próximo token Haskell válido, não apenas números). No entanto, a tupla resultante é agrupada em uma lista, portanto, usamos !!0o primeiro elemento da lista.
  • init.tail.fst.([]%)é a função principal que pega uma string e se aplica %a uma lista vazia. Por exemplo, para uma entrada "(1,2)", aplicando ([]%)rendimentos ("((,)1 2)",""), para que a tupla externa e os colchetes precisem ser removidos. fstobtém o primeiro elemento da tupla, tailremove o colchete de fechamento e inito de abertura.

Edit: Muito obrigado a @ Ørjan Johansen por jogar um total de 21 bytes !


1 Na verdade, o tipo é (Num t1, Num t) => (t, t1) , mas essa é uma história diferente.

2 Ignorando funções polimórficas como id , que não podem realmente funcionar com suas entradas.

Laikoni
fonte
1
Alguém poderia escrever uma função polimórfica usando uma classe de tipo Desugarable, mas seria necessário declarar instâncias para Inte todos os tipos de tupla.
Bergi 4/09/17
1
gpode ser encurtado foldr1(\x r->x++[' '|x>k,r>k]++r)e embutido.
Ørjan Johansen
@ Bergi:… e não se pode declarar instâncias para todos os tipos de tupla . :-) (Experimente: show (1,2,3,4,5,6,7,8,9,0,1,2,3,4,5)na GHCi, em seguida, adicione um ,6no final e tente novamente.)
wchargin
1
Melhorando o inlining para mais seis bytes: use m<-y:l, dobre à esquerda em vez de à direita e use [x]como valor inicial. Experimente online!
Ørjan Johansen
1
fpode ser anônimo: init.tail.fst.([]%).
Ørjan Johansen
11

Haskell, 141 bytes138 bytes (Graças a Ørjan Johansen)

import Language.Haskell.TH
f(TupE l)='(':tail(","<*l)++')':""%l
q%(LitE(IntegerL i):l)=q++show i++" "%l
_%(e:l)='(':f e++')':""%l
_%[]=[]

ftem o tipo Exp -> String.

  • Entrada: uma recessão de Haskell de modeloExp (ou seja, a representação AST padrão de valores de Haskell de tipo arbitrário - basicamente, analisou o código de Haskell antes da verificação de tipo); deve representar uma tupla contendo apenas números inteiros não negativos e outras tuplas.

  • Saída: uma sequência que contém a sintaxe desejada para essa expressão de tupla.

Demo:

$ ghci TupDesugar.hs 
GHCi, version 8.3.20170711: http://www.haskell.org/ghc/  :? for help
Loaded GHCi configuration from /home/sagemuej/.ghc/ghci.conf
Loaded GHCi configuration from /home/sagemuej/.ghci
[1 of 1] Compiling Main             ( TupDesugar.hs, interpreted )
Ok, 1 module loaded.
*Main> :set -XTemplateHaskell -XQuasiQuotes
*Main> f <$> runQ [|(1,2)|]
"(,)1 2"
*Main> f <$> runQ [|(1,2,3)|]
"(,,)1 2 3"
*Main> f <$> runQ [|((1,2),3)|]
"(,)((,)1 2)3"
*Main> f <$> runQ [|(1,2,3,4)|]
"(,,,)1 2 3 4"
*Main> f <$> runQ [|(1,(2,3))|]
"(,)1((,)2 3)"
*Main> f <$> runQ [|(10,1)|]
"(,)10 1"
deixou de girar contra-relógio
fonte
2
Você pode mudar ")"++para ')':em dois lugares e economizar espaço depois tailmovendo-o para fora dos parênteses.
Ørjan Johansen
7

Haskell , 119 bytes

data T=I Int|U[T]
f(U t)="(("++init(t>>",")++')':foldr(\x y->f x++[' '|f x>",",y>","]++y)")"t
f(I n)=show n
init.tail.f

Experimente online! Isso usa um tipo de dados personalizado Tpara representar tuplas, ou seja, uma tupla ((1,2),3)é representada como U[U[I 1,I 2],I 3]. Exemplo de uso: init.tail.f $ U[U[I 1,I 2],I 3]rendimentos (,)((,)1 2)3.

Laikoni
fonte
6

Python 2 , 110 bytes

def f(t):
 s='('+','*~-len(t)+')';c=0
 for i in t:
	try:s+=' '*c+`+i`;c=1
	except:s+='(%s)'%f(i);c=0
 return s

Experimente online!

Leva um tuple.

Erik, o Outgolfer
fonte
4

GNU sed, 149 82 + 2 = 84 bytes

+2 bytes para -rsinalizador.

y/(),/<>'/
:
s/([^<>']+)'/,\1 /
t
s/ ?<(,+)([^>]+)>/((\1)\2)/
t
s/^.|(\)) |.$/\1/g

Experimente online!

Explicação

y/(),/<>'/                   # Replace parens and commas with brackets and apostrophes
:
  s/([^<>']+)'/,\1 /.          # Remove each apostrophe and insert comma after <
  t                            # Branch to : if substitution was made
  s/ ?<(,+)([^>]+)>/((\1)\2)/  # Change <,,,...> to ((,,,)...)
  t                            # Branch to : if substitution was made
s/^.|(\)) |.$/\1/g           # Remove outermost ()s and extra spaces
Jordânia
fonte
Isso falha em alguns casos mais complicados: ((1,(2,3)),4,(5,6))e (1,(2,3),4).
Ørjan Johansen
@ ØrjanJohansen Boa captura. Vou dar uma olhada depois do café da manhã.
Jordan
3

JavaScript, 75 bytes

f=a=>`(${t=a.map(x=>'')})${a.map(v=>t=1/v?1/t?' '+v:v:`(${f(v)})`).join``}`

Matriz de entrada do número | matriz, sequência de saída.

Graças a Neil, economize 2 bytes

tsh
fonte
(1/t?' ':0)+vpode ser 1/t?' '+v:v.
Neil
2

Mathematica, 94 bytes

{"(",","&/@Most@#,")",c=1>0;(xIf[j=ListQ@x,c=j;"("<>#0@x<>")",If[c,c=j;x," "<>x]])/@#}<>""&

Contém uma função U+F4A1interna não imprimível Function.

Retorna um Listnúmero inteiro Strings. Se este não for permitido, isto pode ser corrigido através da adição de mais 10 bytes (esta versão tem um Listde Lists / Integers):

{"(",","&/@Most@#,")",c=1>0;(xIf[j=ListQ@x,c=j;"("<>#0@x<>")",If[c,c=j;""," "]<>ToString@x])/@#}<>""&
JungHwan Min
fonte
2

Pip , 45 bytes

{Y"()"b:yJ',X#a-1Fcab.:c>0?s.cyJ(fc)bR") "')}

Esta é uma função que recebe uma lista como argumento. Experimente online!

Versão comentada

; Define an anonymous function (the argument is available inside as the variable a)
{
  ; Yank the string "()" into y variable
  Y "()"
  ; Create a string of len(a)-1 commas, join y on it, and assign to b
  b: y J ',X#a-1
  ; For each item c in a
  F c a
    ; Concatenate to b the following expression
    b .:
      ; Is c integer or list?
      ; (If c is a positive integer, c>0 is true; but if c is a list, c>0 is false)
      c>0 ?
        ; If c is integer, concatenate space followed by c
        s.c
        ; If c is list, call function recursively on c and use the result to join y
        yJ(fc)
  ; Replace ") " with ")" in b and return the resulting string
  b R ") " ')
}
DLosc
fonte
2

JavaScript (ES6), 88 84 bytes

f=a=>a.reduce((s,e)=>s+=e[0]?`(${f(e)})`:/\)$/.test(s)?e:' '+e,`(${[...a].fill``})`)

Toma uma matriz de números inteiros e matrizes. Editar: salvou 1 byte usando em s+=vez de dois usos separados de s+. Salvei mais 3 bytes agora que posso simplificar o ternário interno. Se eu roubar as idéias do @ tsh, posso reduzi-lo para 76 bytes:

f=a=>a.reduce((s,e)=>s+=t=1/e?1/t?' '+e:e:`(${f(e)})`,`(${t=a.map(_=>``)})`)
Neil
fonte
Your program should take either a tuple or a string representing a sugary tupleEu presumo que uma matriz de matrizes / números inteiros deve estar bem.
JungHwan Min 4/17
1
Claro que é permitido
Assistente de trigo
1

R, 316 bytes?

(Tenho que sair e não tenho certeza da maneira correta de contar bytes ... além disso, não é uma ótima solução, mas queria publicá-la, pois passei o tempo fazendo isso ...)

p=function(x){
x=eval(parse(text=gsub("\\(","list(",x)))
f=function(j,r=T){
p=paste
s=if(r){"("}else{"(("}
o=paste0(s,p(rep(",",length(j)-1),collapse=""),")")
n=lengths(j)
for(i in seq_along(n)){
v=j[[i]]
if(n[i]>1){v=f(v,F)}
o=p(o,v)}
if(!r){o=p(o,")")}
o=gsub(" *([()]) *","\\1",o)
return(o)}
f(x)
}

Casos de teste:

> p("(1,2)")
[1] "(,)1 2"
> p("(1,2,3)")
[1] "(,,)1 2 3"
> p("((1,2),3)")
[1] "(,)((,)1 2)3"
> p("(1,2,3,4)")
[1] "(,,,)1 2 3 4"
> p("(1,(2,3))")
[1] "(,)1((,)2 3)"
> p("(10,1)")
[1] "(,)10 1"
Dason
fonte
São 301 bytes: Experimente online!
Laikoni 5/09/17
2
Golfeou para 261 bytes . Eu deixaria uma explicação para o que mudei, mas, ironicamente, também tenho que ir ... Mas +1, eu não conseguia entender nada disso; bom trabalho!
Giuseppe
0

JavaScript (ES6), 72 bytes

f=(a,b="",c="")=>a.map?b+"("+a.map(x=>'')+")"+a.map(x=>f(x,"(",")"))+c:a

Entrada: matriz contendo números e / ou matrizes

Saída: string

Uso: f ([...])

Conclui todos os casos de teste, melhorias são bem-vindas

ES6_is_awesome
fonte
0

Bytes C, 308 ou 339

#include <ctype.h>
#define p putchar
f(s,e,c,i,l)char*s,*e,*c;{i=1,l=40;if(*s++==l){p(l);for(c=s;i;i+=*c==l,i-=*c==41,i+*c==45&&p(44),c++);p(41);}for(;s<e;s=c){for(i=0;isdigit(*s);s+=*s==44)for(i&&p(32),i=1;isdigit(*s);s++)p(*s);*s==l&&p(l);for(c=s,i=1;++c,c<=e&&i;i+=*c==l)i-=*c==41;f(s,c-1);*s==l&&p(41);}}
#define g(x) f(x, x+strlen(x))

308 ou 339 bytes, dependendo da passagem ou não de um ponteiro para o final da sequência de entrada; a última linha existe apenas para permitir a passagem direta de uma string literal sem ter que calcular seu comprimento.

Explicação

Um algoritmo bastante simples. Ele conta o número de vírgulas na profundidade atual, as imprime como um construtor de tupla e, em seguida, acompanha os argumentos da tupla, escapados (espaços entre números, tuplas aninhadas entre parênteses), recursivamente.

#include <stdio.h>
#include <ctype.h>
typedef enum { false, true } bool;

void tup2ptsfree(char *s, char *e)
{
  int depth;
  char *c;

  if (*s++ == '(') { /* If we are at the start of a tuple, write tuple function `(,,,)` (Otherwise, we are at a closing bracket or a comma) */
    putchar('(');
    /* do the search for comma's */
    c=s; /* probe without moving the original pointer */
    for (depth=1; depth != 0; c++) {
      if (*c == '(') depth++;
      if (*c == ')') depth--;
      if (*c == ',' && depth == 1) putchar(','); /* We have found a comma at the right depth, print it */
    }
    putchar(')');
  }
  while (s < e) { /* The last character is always ')', we can ignore it and save a character. */
    bool wroteNumber;
    for (wroteNumber=false; isdigit(*s); wroteNumber = true) {
      if (wroteNumber) p(' ');           /* If this is not the first number we are writing, add a space */
      while (isdigit(*s)) putchar(*s++); /* Prints the entire number */
      if (*s == ',') s++;                /* We found a ',' instead of a ')', so there might be more numbers following */
    }
    /* Add escaping parenthesis if we are expanding a tuple (Using a small if statement instead of a large branch to prevent doing the same thing twice, since the rest of the code is essentially the same for both cases). */
    if (*s == '(') putchar('(');
    /* Find a matching ')'... */
    c=s+1;
    for (depth=1; c <= e && depth != 0; c++) {
      if (*c == '(') depth++;
      if (*c == ')') depth--;
    }
    /* Found one */
    /* Note how we are looking for a matching paren twice, with slightly different parameters. */
    /* I couldn't find a way to golf this duplication away, though it might be possible. */
    /* Expand the rest of the tuple */
    tup2ptsfree(s, c-1);
    /* idem */
    if (*s == '(') putchar(')');
    /* Make the end of the last expansion the new start pointer. */
    s=c;
  }
}

#define h(x) tup2ptsfree(x, x+strlen(x))

Casos de teste e aplicação

#include <stdio.h>

#define ARRAYSIZE(arr) (sizeof(arr)/sizeof(*arr))
static char *examples[] = {
  "(1,2)",
  "(10,1)",
  "(1,2,3)",
  "(1,2,3,4)",
  "((1,2),3)",
  "(1,(2,3))",
  "(1,(2,3),4)",
  "((1,2),(3,4))",
  "((1,(2,3)),4,(5,6))",
  "((1,((2,3), 4)),5,(6,7))",
  "(42,48)",
  "(1,2,3,4,5,6,7)"
};

int main(void)
{
  int i;
  for (i=0; i < ARRAYSIZE(examples); i++) {
    printf("%-32s | \"", examples[i]);
    g(examples[i]); /* Test with golfed version */
    printf("\"\n");
    printf("%-32s | \"", examples[i]);
    h(examples[i]); /* Test with original version */
    printf("\"\n");
  }
}
YoYoYonnY
fonte