让我们做Diet Haskell


21

Haskell有可以写成的元组

(a,b,c)

但这只是语法糖

(,,)a b c

在一般的Ñ元组可以与形成n-1个 , S之间(... )接着它的元素用空格分开。例如7元组(1,2,3,4,5,6,7)可以由

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

由于Haskell没有1元组,因此无法形成它们。您也不会对空元组负责。

嵌套元组可以使用括号来覆盖操作顺序。

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

作为我们从Haskell删除所有语法糖的追求的一部分, 我要请您编写一个程序,从Haskell的元组中删除语法糖。

您的程序应采用一个元组,一个数组或一个表示含糖元组的字符串,并应输出一个表示“无糖”元组的字符串。输入元组将只包含正整数或其他元组。

由于我们在这里打高尔夫球,您的输出应该很短。它不应包含不必要的内容

  • 空格。空格仅应用于分隔元组函数的参数,并且不应出现在a )或a之后(

  • 括号。仅在形成元组函数或嵌套元组时才应使用括号。

这是一个问题,因此答案将以字节计分,而字节数越少越好。

测试用例

(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

如果我什么都不缺,那么您将覆盖1个元组,但不覆盖空元组。空元组有效输入吗?
–totalhuman

3
@totallyhuman您不必处理空的元组。
小麦巫师

第五个测试用例有一个额外的要求,
H.PWiz

2
同样,“数字”是指“正整数”吗?
Erik the Outgolfer

2
建议的测试用例:((1,(2,3)),4,(5,6))(1,(2,3),4)
与Orjan约翰森

Answers:


17

哈斯克尔169个 148字节

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

在线尝试!将元组作为字符串。init.tail.fst.([]%)是匿名主要功能。将其绑定到例如f并使用like f "(3,(14,1),4,7)",这样就产生了"(,,,)3((,)14 1)4 7"

您为什么不将输入作为Haskell元组提供?因为Haskell是强类型的,所以元组的(1,2)类型为(Int,Int)1,而元组的(1,(2,3))类型为(Int,(Int,Int))。因此,不能将接受第一种元组的函数应用于第二种元组,尤其是不能存在采用任意元组2的函数。

说明:

  • p:k="(,"是分配一小段路p'(',并k","
  • (%)是递归解析和转换功能。第一个参数是已解析的元组条目的列表,第二个参数是原始字符串的其余部分。每个调用都返回当前转换后的元组的一个元组(作为一个字符串,并括在方括号中)以及该字符串的其余部分。
    • l%('(':r)如果字符串以左方括号开头,则需要解析一个新的元组条目。
      (y,x:s)<-[]%r我们递归地应用%并获得一个元组条目y,其余的字符串分成下一个字符x和其余字符串s
      m<-y:l我们将新条目添加y到已找到条目的当前列表中,l并调用结果m
    • 现在,下一个字符x是逗号,或右括号)。这last$ <B> :[ <A> |x<',']只是一种较短的书写方式if x == ')' then <A> else <B>
    • 因此,如果a ,是下一个,则需要递归地解析下一个条目:m%(p:s)为了使在合适的情况下结束并传递已找到的条目的列表,我们在括号前面加了一个中括号m
    • 否则x == ')',我们完成了当前的元组,需要进行所需的转换:(p:p:(l>>k)++x:foldl(\r x->x++[' '|x>k,r>k]++r)[x]m,s)
      • p:p:(l>>k)++x:如果我们找到了n个条目,则mn个元素,并且y,在添加最近找到的元素之前的列表中,有n-1个条目。这很方便,因为我们需要n-1 ,n元素元组,并l>>k在列表上工作为“将列表ky元素自身的连接次数与元素的连接次数相同”。因此,第一部分产生一些类似的字符串"((,,,)"
      • foldl(\r x->x++[' '|x>k,r>k]++r)[x]m串联元素m(以相反的顺序,因为通过向其前面添加新条目m本身以相反的顺序构造),同时如果两个元素都是数字,则仅在两个元素之间添加空格:通过按字典顺序比较,[' '|x>k,r>k]我们检查当前条目xr是数字它们到","-如果它们不是数字,则它们已经是一个用括号括起来的元组表示形式并'(' < ','成立。
    • 如果l%('(':r)开头的模式匹配失败,那么我们将在最后一行结束l%r=lex r!!0。这意味着我们需要解析一个数字并返回该数字和字符串的其余部分。幸运的是,有一个lex函数可以做到这一点(它解析下一个有效的Haskell令牌,而不仅仅是数字)。但是,生成的元组被包装到一个列表中,因此我们用于!!0获取列表的第一个元素。
  • init.tail.fst.([]%)是主要函数,它接受字符串并对其应用%空列表。例如"(1,2)",使用([]%)yield 的input ,,则("((,)1 2)","")需要删除外部元组和括号。fst获取元组的第一个元素,tail删除右括号和左括号init

编辑:非常感谢@ØrjanJohansen打高尔夫球,总共21个字节


1实际上,类型是(Num t1,Num t)=>(t,t1),但这是一个不同的故事。

2忽略id等多态函数,这些函数实际上无法使用其输入。


1
一个人可以使用typeclass编写一个多态函数Desugarable,但是一个人必须为Int所有元组类型声明实例。
Bergi

1
g可以缩短foldr1(\x r->x++[' '|x>k,r>k]++r)并内联。
与Orjan约翰森

@Bergi:…并且不能所有元组类型声明实例。:-)(尝试:show (1,2,3,4,5,6,7,8,9,0,1,2,3,4,5)在GHCi中,然后,6在末尾添加a,然后重试。)
wchargin

1
改进内联六个字节:使用m<-y:l,左折叠而不是右折叠,并[x]用作初始值。在线尝试!
与Orjan约翰森

1
f可以匿名:init.tail.fst.([]%)
与Orjan约翰森

11

哈斯克尔, 141字节138个字节(感谢ØrjanJohansen)

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

f具有类型Exp -> String

  • 输入:模板HaskellExp解析度(即,任意类型的Haskell值的标准AST表示-基本上是在类型检查之前解析的Haskell代码);必须表示仅包含非负整数和其他此类元组的元组。

  • 输出:一个字符串,其中包含该元组表达式的已终止语法。

演示:

$ 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
您可以在两个位置更改")"++')':,然后tail通过将其移到括号外来节省空间。
与Orjan约翰森

7

Haskell,119个字节

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

在线尝试!这使用自定义数据类型T表示元组,即元组((1,2),3)表示为U[U[I 1,I 2],I 3]。用法示例:init.tail.f $ U[U[I 1,I 2],I 3]yields (,)((,)1 2)3



4

GNU sed,149 82 + 2 = 84字节

+2个字节用于-r标志。

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

在线尝试!

说明

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

在某些更复杂的情况下,此操作将失败:((1,(2,3)),4,(5,6))(1,(2,3),4)
与Orjan约翰森

@ØrjanJohansen不错。早餐后我来看一下。
乔丹

3

JavaScript,75个字节

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

输入数字数组,输出字符串。

感谢Neil,节省了2个字节


(1/t?' ':0)+v可以1/t?' '+v:v
尼尔

2

Mathematica,94个字节

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

包含一个不可打印U+F4A1的内置Function函数。

取一个List整数Strings。如果这是不允许的,这可以通过添加10多个字节是固定的(这个版本需要ListListS / IntegerS):

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

2

,45字节

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

这是一个将列表作为参数的函数。在线尝试!

评论版本

; 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字节

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

接受整数和数组组成的数组。编辑:通过使用s+=而不是两个单独的使用来保存1个字节s+。现在可以再保存3个字节,以便简化内部三进制。如果我窃取@tsh的想法,则可以将其缩减为76个字节:

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 tuple我认为数组/整数的数组应该没问题。
JungHwan Min'9

1
当然可以
麦迪奇向导

1

R,316个字节?

(必须注意并不确定计数字节的正确方法……再加上这不是一个很好的解决方案,但由于我花了很多时间才想发布它……)

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

测试用例:

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


2
打到261字节。我将对所做的更改进行解释,但具有讽刺意味的是,我必须离开...但是+1,我根本无法解决这个问题;做得好!
朱塞佩

0

JavaScript(ES6),72个字节

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

输入:包含数字和/或数组的数组

输出:字符串

用法:f([...])

完成所有测试用例,欢迎改进


0

C,308或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或339个字节,具体取决于是否允许将指针传递到输入字符串的末尾;最后一行仅用于允许直接传递字符串文字而不必计算其长度。

说明

一个非常简单的算法。它计算当前深度处的逗号数量,将它们打印为元组构造函数,然后递归地处理元组的参数,然后进行转义(数字之间的空格,括号之间的嵌套元组)。

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

测试用例及应用

#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");
  }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.