Lassen Sie uns Diet Haskell machen

21

Haskell hat Tupel, die als geschrieben werden können

(a,b,c)

Dies ist jedoch nur syntaktischer Zucker für

(,,)a b c

Im Allgemeinen kann ein n- Tupel mit n-1 , s zwischen (... )und den durch Leerzeichen getrennten Elementen gebildet werden. Beispielsweise kann das 7-Tupel (1,2,3,4,5,6,7)durch gebildet werden

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

Da Haskell keine 1-Tupel hat, können sie nicht gebildet werden. Sie sind auch nicht für leere Tupel verantwortlich.

Verschachtelte Tupel können mithilfe von Parens gebildet werden, um die Reihenfolge der Operationen zu überschreiben.

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

Als Teil unseres Strebens, allen syntaktischen Zucker aus Haskell zu entfernen, werde ich Sie bitten, ein Programm zu schreiben, das syntaktischen Zucker auch aus Haskells Tupeln entfernt.

Ihr Programm sollte ein Tupel, ein Array oder eine Zeichenfolge, die ein zuckerhaltiges Tupel darstellt, verwenden und eine Zeichenfolge ausgeben, die ein "zuckerfreies" Tupel darstellt. Eingabetupel enthalten immer nur positive ganze Zahlen oder andere Tupel.

Da wir hier Golf spielen, sollte Ihre Leistung kurz sein. Es sollte nicht unnötig enthalten

  • Räume. Leerzeichen sollten nur verwendet werden, um Argumente von Tupelfunktionen zu trennen. Sie sollten nicht nach )oder vor a stehen(

  • Klammern. Klammern sollten nur beim Bilden von Tupelfunktionen oder beim Verschachteln von Tupeln verwendet werden.

Dies ist eine Frage, daher werden die Antworten in Bytes bewertet, wobei weniger Bytes besser sind.

Testfälle

(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
Weizen-Assistent
quelle
Wenn ich nichts verpasse, decken Sie 1-Tupel, aber keine leeren Tupel ab. Sind leere Tupel gültige Eingaben?
Totalhuman
3
@totallyhuman Sie müssen nicht mit leeren Tupeln umgehen.
Weizen-Assistent
Der 5. Testfall hat ein Extra,
H.PWiz
2
Auch mit "Zahlen" meinen Sie "positive ganze Zahlen"?
Erik der Outgolfer
2
Vorgeschlagene Testfälle: ((1,(2,3)),4,(5,6))und (1,(2,3),4).
Ørjan Johansen

Antworten:

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

Probieren Sie es online! Nimmt das Tupel als String. init.tail.fst.([]%)ist die anonyme Hauptfunktion. Binden Sie es an zB fund verwenden Sie gerne f "(3,(14,1),4,7)", was ergibt "(,,,)3((,)14 1)4 7".

Warum wird die Eingabe nicht als Haskell-Tupel bereitgestellt? Da Haskell stark typisiert ist, hat ein Tupel (1,2)den Typ (Int,Int)1 und ein Tupel (1,(2,3))den Typ (Int,(Int,Int)). Somit kann eine Funktion, die die erste Art von Tupel akzeptiert, nicht auf die zweite Art angewendet werden, und insbesondere kann es keine Funktion geben, die ein beliebiges Tupel 2 akzeptiert .

Erläuterung:

  • p:k="(,"ist ein kurzer Weg zuweisen pzu '('und kzu ",".
  • (%)ist die rekursive Analyse- und Konvertierungsfunktion. Das erste Argument ist eine Liste bereits geparster Tupeleinträge, das zweite Argument ist der Rest der ursprünglichen Zeichenfolge. Jeder Aufruf gibt ein Tupel des aktuell konvertierten Tupels (als Zeichenfolge und in Klammern) und den Rest der Zeichenfolge zurück.
    • l%('(':r)Wenn die Zeichenfolge mit einer öffnenden Klammer beginnt, müssen wir einen neuen Tupeleintrag analysieren.
      (y,x:s)<-[]%rWir wenden rekursiv an %und erhalten einen Tupeleintrag yund die verbleibende Zeichenfolge wird in das nächste Zeichen xund den Rest der Zeichenfolge aufgeteilt s.
      m<-y:lWir fügen den neuen Eintrag yder aktuellen Liste der bereits gefundenen Einträge hinzu lund rufen das Ergebnis auf m.
    • Das nächste Zeichen xist jetzt entweder ein Komma ,oder eine schließende Klammer ). Das last$ <B> :[ <A> |x<',']ist nur eine kürzere Schreibweise if x == ')' then <A> else <B>.
    • Wenn also a ,als nächstes kommt, müssen wir den nächsten Eintrag rekursiv analysieren: m%(p:s)Wir stellen eine öffnende Klammer voran, um im richtigen Fall zu landen und die Liste der bereits gefundenen Einträge zu übergeben m.
    • Andernfalls x == ')'haben wir das aktuelle Tupel beendet und müssen die erforderliche Transformation durchführen:(p:p:(l>>k)++x:foldl(\r x->x++[' '|x>k,r>k]++r)[x]m,s)
      • p:p:(l>>k)++x:Wenn wir n Einträge gefunden haben, mhat die Liste vor dem Hinzufügen des zuletzt gefundenen Elements n Elemente und n-1 Einträge. Dies ist praktisch, da wir n-1 für ein Elementtupel benötigen und Listen so bearbeiten, dass "die Liste mit sich selbst so oft verkettet wird, wie es Elemente gibt" . Somit ergibt dieser erste Teil einen String wie .y ,nl>>kky"((,,,)"
      • foldl(\r x->x++[' '|x>k,r>k]++r)[x]mVerkettet die Elemente von m(in umgekehrter Reihenfolge, da durch Hinzufügen neuer Einträge an der Vorderseite mselbst in umgekehrter Reihenfolge erstellt wurde), während nur Leerzeichen zwischen zwei Elementen hinzugefügt werden, wenn es sich um beide Zahlen handelt: [' '|x>k,r>k]Wir überprüfen durch lexikografischen Vergleich , ob die aktuellen Einträge xund rZahlen sind sie zu ","- wenn sie keine Zahlen sind, sind sie bereits eine Tupel-Darstellung in eckigen Klammern und '(' < ','halten.
    • Wenn das Muster Spiel l%('(':r)gleich zu Beginn ausfällt, dann enden wir in der letzten Zeile nach oben: l%r=lex r!!0. Das heißt, wir müssen eine Zahl analysieren und die Zahl und den Rest der Zeichenfolge zurückgeben. Zum Glück gibt es die lexFunktion, die genau das macht (sie analysiert das nächste gültige Haskell-Token, nicht nur Zahlen). Das resultierende Tupel wird jedoch in eine Liste eingeschlossen, sodass wir !!0das erste Element der Liste abrufen.
  • init.tail.fst.([]%)ist die Hauptfunktion, die eine Zeichenfolge akzeptiert und %mit einer leeren Liste darauf angewendet wird. ZB für eine Eingabe "(1,2)", die ([]%)Ausbeuten anwendet ("((,)1 2)",""), müssen das äußere Tupel und die Klammern entfernt werden. fstRuft das erste Element des Tupels ab, tailentfernt die schließende und initdie öffnende Klammer .

Edit: Vielen Dank an @ Ørjan Johansen für das Golfen von insgesamt 21 Bytes !


1 Eigentlich ist der Typ (Num t1, Num t) => (t, t1) , aber das ist eine andere Geschichte.

2 Ignorieren Sie polymorphe Funktionen wie id , die mit ihren Eingaben nicht funktionieren.

Laikoni
quelle
1
Man könnte eine polymorphe Funktion mit einer Typenklasse schreiben Desugarable, aber man müsste Instanzen für Intund alle Tupeltypen deklarieren .
Bergi
1
gkann gekürzt foldr1(\x r->x++[' '|x>k,r>k]++r)und inliniert werden.
Ørjan Johansen
@Bergi: ... und man kann nicht Instanzen deklarieren für wirklich alle Tupel - Typen . :-) (Versuchen Sie: show (1,2,3,4,5,6,7,8,9,0,1,2,3,4,5)in GHCi, fügen Sie dann ,6am Ende eine hinzu und versuchen Sie es erneut.)
wchargin
1
Verbesserung des Inlinings für sechs weitere Bytes: Verwenden Sie m<-y:l, falten Sie links statt rechts und verwenden Sie [x]als Anfangswert. Probieren Sie es online!
Ørjan Johansen
1
fkann anonym bleiben: init.tail.fst.([]%).
Ørjan Johansen
11

Haskell, 141 Bytes138 Bytes (Dank an Ø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
_%[]=[]

fhat Typ Exp -> String.

  • Eingabe: eine Template-Haskell-Exp Ression (dh die Standard-AST-Darstellung von Haskell-Werten beliebigen Typs - im Grunde analysiert Haskell-Code vor der Typüberprüfung); muss ein Tupel darstellen, das nur nichtnegative Ganzzahlen und andere solche Tupel enthält.

  • Ausgabe: Eine Zeichenfolge, die die Syntax für den Tupelausdruck enthält.

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"
hörte auf, sich gegen den Uhrzeigersinn zu drehen
quelle
2
Sie können ")"++an ')':zwei Stellen zu wechseln und den Platz nach dem zu speichern, tailindem Sie ihn außerhalb der Klammern verschieben.
Ø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

Probieren Sie es online! Dies verwendet einen benutzerdefinierten Datentyp T, um Tupel darzustellen, dh ein Tupel ((1,2),3)wird als dargestellt U[U[I 1,I 2],I 3]. Anwendungsbeispiel: init.tail.f $ U[U[I 1,I 2],I 3]Erträge (,)((,)1 2)3.

Laikoni
quelle
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

Probieren Sie es online!

Nimmt ein tuple.

Erik der Outgolfer
quelle
4

GNU sed, 149 82 + 2 = 84 Bytes

+2 Bytes für -rFlag.

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

Probieren Sie es online!

Erläuterung

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
Jordan
quelle
Dies schlägt in einigen komplizierteren Fällen fehl: ((1,(2,3)),4,(5,6))und (1,(2,3),4).
Ørjan Johansen
@ ØrjanJohansen Guter Fang. Ich werde mich nach dem Frühstück umsehen.
Jordanien
3

JavaScript, 75 Byte

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

Eingabearray von Nummer | Array, Ausgabezeichenfolge.

Dank Neil sparen Sie 2 Bytes

tsh
quelle
(1/t?' ':0)+vkann sein 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]])/@#}<>""&

Enthält eine nicht druckbare U+F4A1 , eingebaute FunctionFunktion.

Nimmt eine Listganze Zahl Strings. Wenn dies nicht erlaubt ist, kann dies durch Hinzufügen von 10 weiteren Bytes behoben werden (diese Version benötigt eine Listvon Lists / Integers):

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

Pip , 45 Bytes

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

Dies ist eine Funktion, die eine Liste als Argument verwendet. Probieren Sie es online!

Kommentierte Version

; 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
quelle
2

JavaScript (ES6), 88 84 Bytes

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

Nimmt ein Array von ganzen Zahlen und Arrays. Bearbeiten: 1 Byte durch Verwendung von s+=anstelle von zwei separaten Verwendungen von gespeichert s+. Sparte jetzt weitere 3 Bytes, da ich das innere Ternär vereinfachen kann. Wenn ich @ tshs Ideen stehle, kann ich es auf 76 Bytes reduzieren:

f=a=>a.reduce((s,e)=>s+=t=1/e?1/t?' '+e:e:`(${f(e)})`,`(${t=a.map(_=>``)})`)
Neil
quelle
Your program should take either a tuple or a string representing a sugary tupleIch würde annehmen, dass ein Array von Arrays / ganzen Zahlen in Ordnung sein sollte.
JungHwan Min
1
Sicher ist das erlaubt
Wheat Wizard
1

R 316 Bytes?

(Ich bin mir nicht sicher, wie ich die Bytes richtig zählen soll. Außerdem ist es keine großartige Lösung, aber ich wollte es veröffentlichen, da ich die Zeit damit verbracht habe, es zu machen.)

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

Testfälle:

> 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
quelle
Es ist 301 Bytes: Probieren Sie es online!
Laikoni
2
Golf zu 261 Bytes . Ich würde eine Erklärung hinterlassen für das, was ich geändert habe, aber ironischerweise muss ich auch gehen ... Aber +1, ich konnte meinen Kopf überhaupt nicht darum wickeln; gute Arbeit!
Giuseppe
0

JavaScript (ES6), 72 Byte

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

Eingabe: Array mit Zahlen und / oder Arrays

Ausgabe: String

Verwendung: f ([...])

Beendet alle Testfälle, Verbesserungen erwünscht

ES6_ist_schön
quelle
0

C, 308 oder 339 Bytes

#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 oder 339 Byte, abhängig davon, ob die Übergabe eines Zeigers an das Ende der Eingabezeichenfolge zulässig ist oder nicht; Die letzte Zeile dient nur dazu, ein String-Literal direkt zu übergeben, ohne seine Länge berechnen zu müssen.

Erläuterung

Ein ziemlich einfacher Algorithmus. Es zählt die Anzahl der Kommas in der aktuellen Tiefe, gibt sie als Tupelkonstruktor aus und folgt dann rekursiv den Argumenten des Tupels (Leerzeichen zwischen Zahlen, geschachtelte Tupel in Klammern).

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

Testfälle und Anwendung

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