Facciamo Diet Haskell


21

Haskell ha tuple che possono essere scritte come

(a,b,c)

Tuttavia, questo è solo zucchero sintattico per

(,,)a b c

In generale una n tupla può essere formata con n-1 , s tra (... )seguita dai suoi elementi separati da spazi. Ad esempio la 7-tupla, (1,2,3,4,5,6,7)può essere formata da

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

Poiché Haskell non ha 1 tupla, non possono essere formati. Non sarai inoltre ritenuto responsabile per le tuple vuote.

Le tuple nidificate possono essere formate usando le parentesi per sovrascrivere l'ordine delle operazioni.

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

Come parte della nostra ricerca per rimuovere tutto lo zucchero sintattico da Haskell, ti chiederò di scrivere un programma che rimuova lo zucchero sintattico anche dalle tuple di Haskell.

Il tuo programma dovrebbe prendere una tupla, un array o una stringa che rappresenta una tupla zuccherina e dovrebbe generare una stringa che rappresenta una tupla "senza zucchero". Le tuple di input conterranno sempre numeri interi positivi o altre tuple.

Dato che stiamo giocando a golf qui il tuo risultato dovrebbe essere breve. Non dovrebbe contenere inutili

  • Spazi. Gli spazi devono essere usati solo per separare gli argomenti di una funzione tupla e non devono apparire dopo )o prima di a(

  • Parentesi. Le parentesi devono essere utilizzate solo quando si formano funzioni di tupla o quando si annidano le tuple.

Questa è una domanda di quindi le risposte verranno classificate in byte con meno byte migliori.

Casi test

(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

Se non mi manca nulla, copri 1 tupla ma non le tuple vuote ..? Le tuple vuote sono input validi?
totalmente umano il

3
@totallyhuman Non devi gestire le tuple vuote.
Wheat Wizard

Il 5 ° test ha un extra,
H.Piz,

2
Anche per "numeri" intendi "numeri interi positivi"?
Erik the Outgolfer,

2
Casi di prova suggeriti: ((1,(2,3)),4,(5,6))e (1,(2,3),4).
Ørjan Johansen,

Risposte:


17

Haskell , 169 148 byte

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

Provalo online! Prende la tupla come una stringa. init.tail.fst.([]%)è la funzione principale anonima. Legalo ad esempio fe usa like f "(3,(14,1),4,7)", che produce "(,,,)3((,)14 1)4 7".

Perché l'input non viene fornito come una tupla di Haskell, chiedi? Poiché Haskell è fortemente tipizzato, una tupla (1,2)ha tipo (Int,Int)1 e una tupla (1,(2,3))ha tipo (Int,(Int,Int)). Pertanto una funzione che accetta il primo tipo di tupla non può essere applicata al secondo tipo, e in particolare non può esserci alcuna funzione che accetta una tupla arbitraria 2 .

Spiegazione:

  • p:k="(,"è un breve modo da assegnare pa '('e ka ",".
  • (%)è la funzione di analisi e conversione ricorsiva. Il primo argomento è un elenco di voci di tupla già analizzate, il secondo argomento è il resto della stringa originale. Ogni chiamata restituisce una tupla dell'attuale tupla convertita (come stringa e racchiusa tra parentesi) e il resto della stringa.
    • l%('(':r)Se la stringa inizia con una parentesi quadra aperta, dobbiamo analizzare una nuova voce tupla.
      (y,x:s)<-[]%rAppliciamo in modo ricorsivo %e otteniamo una voce tupla ye la stringa rimanente divisa nel carattere successivo xe nel resto della stringa s.
      m<-y:lAggiungiamo la nuova voce yall'elenco corrente di voci già trovate le chiamiamo il risultato m.
    • Il carattere successivo xora è una virgola ,o una parentesi quadra di chiusura ). Il last$ <B> :[ <A> |x<',']è solo un modo più breve di scrittura if x == ')' then <A> else <B>.
    • Quindi, se ,è il prossimo, dobbiamo analizzare ricorsivamente la voce successiva: m%(p:s)anteponiamo una parentesi aperta per finire nel caso giusto e passare l'elenco delle voci già trovate m.
    • In caso contrario x == ')', abbiamo terminato l'attuale tupla e dobbiamo eseguire la trasformazione richiesta:(p:p:(l>>k)++x:foldl(\r x->x++[' '|x>k,r>k]++r)[x]m,s)
      • p:p:(l>>k)++x:Se abbiamo trovato n voci, quindi mha n elementi e y, l'elenco prima di aggiungere l'elemento trovato più di recente, ha n-1 voci. Questo è utile in quanto abbiamo bisogno di n-1 , per una ntupla di elementi e l>>kfunziona su liste come "concatenare la lista kcon se stessa quante volte yha elementi" . Quindi questa prima parte produce una stringa simile "((,,,)".
      • foldl(\r x->x++[' '|x>k,r>k]++r)[x]mconcatena gli elementi di m(in ordine inverso, perché aggiungendo nuove voci al fronte mstesso è stato costruito in ordine inverso) mentre si aggiungono solo spazi tra due elementi se sono entrambi numeri: [' '|x>k,r>k]controlliamo se le voci correnti xe rsono numeri confrontando lessicograficamente li a ","- se non sono numeri, sono già una rappresentazione di tupla racchiusa tra parentesi e '(' < ','contiene.
    • Se il pattern match l%('(':r)fin dall'inizio non riesce, allora si finisce sull'ultima riga: l%r=lex r!!0. Ciò significa che dobbiamo analizzare un numero e restituire il numero e il resto della stringa. Fortunatamente c'è la lexfunzione che fa esattamente questo (analizza il prossimo token Haskell valido, non solo numeri). Tuttavia la tupla risultante è racchiusa in un elenco, quindi usiamo !!0per ottenere il primo elemento dell'elenco.
  • init.tail.fst.([]%)è la funzione principale che accetta una stringa e si applica %con un elenco vuoto. Ad esempio per un input "(1,2)", applicando i ([]%)rendimenti ("((,)1 2)",""), è necessario rimuovere la tupla esterna e le parentesi. fstottiene il primo elemento della tupla, tailrimuove la parentesi di chiusura e initquella di apertura.

Modifica: Mille grazie a @ Ørjan Johansen per il golf per un totale di 21 byte !


1 In realtà, il tipo è (Num t1, Num t) => (t, t1) , ma questa è una storia diversa.

2 Ignorando le funzioni polimorfiche come id , che in realtà non può funzionare con il loro input.


1
Si potrebbe scrivere una funzione polimorfica usando una typeclass Desugarable, ma si dovrebbero dichiarare istanze per Inte tutti i tipi di tupla.
Bergi,

1
gpuò essere abbreviato foldr1(\x r->x++[' '|x>k,r>k]++r)e integrato.
Ørjan Johansen,

@Bergi:… e non si possono dichiarare istanze per tutti i tipi di tuple . :-) (Prova: show (1,2,3,4,5,6,7,8,9,0,1,2,3,4,5)in GHCi, quindi aggiungi ,6a alla fine e riprova.)
wchargin

1
Miglioramento dell'inline per altri sei byte: utilizzare m<-y:l, piegare a sinistra anziché a destra e utilizzare [x]come valore iniziale. Provalo online!
Ørjan Johansen,

1
fpuò essere anonima: init.tail.fst.([]%).
Ørjan Johansen,

11

Haskell, 141 byte138 byte (grazie 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
_%[]=[]

fha tipo Exp -> String.

  • Input: una versione di HaskellExp modello (ovvero la rappresentazione AST standard di valori Haskell di tipo arbitrario - fondamentalmente, analizzare il codice Haskell prima del controllo del tipo); deve rappresentare una tupla contenente solo numeri interi non negativi e altre tuple simili.

  • Output: una stringa contenente la sintassi desugared per quell'espressione di 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"

2
È possibile modificare ")"++a ')':in due posti, e salvare lo spazio dopo lo tailspostandola parentesi fuori.
Ørjan Johansen,

7

Haskell , 119 byte

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

Provalo online! Questo utilizza un tipo di dati personalizzato Tper rappresentare le tuple, ovvero una tupla ((1,2),3)è rappresentata come U[U[I 1,I 2],I 3]. Esempio di utilizzo: init.tail.f $ U[U[I 1,I 2],I 3]rese (,)((,)1 2)3.



4

GNU sed, 149 82 + 2 = 84 byte

+2 byte per -rflag.

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

Provalo online!

Spiegazione

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

Questo fallisce in alcuni casi più complicati: ((1,(2,3)),4,(5,6))e (1,(2,3),4).
Ørjan Johansen,

@ ØrjanJohansen Buona cattura. Dare un'occhiata dopo colazione.
Giordania,

3

JavaScript, 75 byte

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

Matrice di input di numero | array, stringa di output.

Grazie a Neil, risparmia 2 byte


(1/t?' ':0)+vpuò essere 1/t?' '+v:v.
Neil,

2

Mathematica, 94 byte

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

Contiene una funzione non stampabile U+F4A1, incorporata Function.

Prende un Listnumero intero String. Se ciò non è consentito, questo può essere risolto aggiungendo altri 10 byte (questa versione richiede uno Listdi Lists / Integers):

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

2

Pip , 45 byte

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

Questa è una funzione che accetta un elenco come argomento. Provalo online!

Versione commentata

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

2

JavaScript (ES6), 88 84 byte

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

Accetta una matrice di numeri interi e array. Modifica: 1 byte salvato utilizzando s+=invece di due usi separati di s+. Ho salvato altri 3 byte ora che posso semplificare il ternario interno. Se rubo le idee di @ tsh, posso ottenerlo fino a 76 byte:

f=a=>a.reduce((s,e)=>s+=t=1/e?1/t?' '+e:e:`(${f(e)})`,`(${t=a.map(_=>``)})`)

Your program should take either a tuple or a string representing a sugary tuplePresumo che una serie di matrici / numeri interi dovrebbe andare bene.
JungHwan Min

1
Sicuro che è permesso
Wheat Wizard

1

R, 316 byte?

(Devo uscire e non sono sicuro del modo corretto di contare i byte ... in più non è un'ottima soluzione ma volevo pubblicarlo da quando ho trascorso il tempo a farlo ...)

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

Casi test:

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

Sono 301 byte: provalo online!
Laikoni,

2
Golfato a 261 byte . Lascerei una spiegazione per ciò che ho cambiato, ma ironicamente, devo anche andare ... Ma +1, non potrei affatto avvolgere la mia testa attorno a questo; bel lavoro!
Giuseppe,

0

JavaScript (ES6), 72 byte

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

Input: array contenente numeri e / o array

Output: stringa

Utilizzo: f ([...])

Completa tutti i casi di test, miglioramenti benvenuti


0

C, 308 o 339 byte

#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 o 339 byte, a seconda che sia consentito o meno il passaggio di un puntatore alla fine della stringa di input; l'ultima riga è lì solo per consentire il passaggio di una stringa direttamente senza dover calcolare la sua lunghezza.

Spiegazione

Un algoritmo piuttosto semplice. Conta il numero di virgole alla profondità corrente, le stampa come un costruttore di tuple, quindi segue gli argomenti della tupla, fuggiti (spazi tra numeri, tuple nidificate tra parentesi), in modo ricorsivo.

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

Casi di prova e applicazione

#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");
  }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.