다이어트 하스켈을 만들자


21

하스켈에는 다음과 같이 쓸 수있는 튜플이 있습니다

(a,b,c)

그러나 이것은 단지 구문 설탕입니다

(,,)a b c

일반적에서, N 개 튜플은 형성 할 수 N-1 , 사이의 (... )요소 뒤에 공간으로 분리된다. 예를 들어 7 튜플 (1,2,3,4,5,6,7)

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

Haskell에는 1- 튜플이 없으므로 형성 할 수 없습니다. 빈 튜플에 대한 책임도지지 않습니다.

중첩 튜플은 작업 순서를 무시하기 위해 Parens를 사용하여 형성 할 수 있습니다.

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

Haskell에서 모든 구문 설탕을 제거하기위한 노력의 일환으로 Haskell의 튜플에서 구문 설탕을 제거하는 프로그램을 작성하도록 요청합니다.

프로그램은 튜플, 배열 또는 단 튜플을 나타내는 문자열을 가져와 "무가당"튜플을 나타내는 문자열을 출력해야합니다. 입력 튜플은 양의 정수 또는 다른 튜플 만 포함합니다.

우리가 여기서 골프를 치기 때문에 당신의 생산량은 짧아야합니다. 불필요하게 포함해서는 안됩니다

  • 공백. 공백은 튜플 함수의 인수를 구분하는 데만 사용해야하며 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- 튜플을 커버하지만 비어있는 튜플은 커버하지 않습니다. 빈 튜플이 유효한 입력입니까?
완전히 인간적인

3
@totallyhuman 빈 튜플을 다룰 필요는 없습니다.
밀 마법사

제 5 회 시험의 경우는 여분을 가지고,
H.PWiz

2
또한 "숫자"는 "양의 정수"를 의미합니까?
Outgolfer Erik

2
권장 테스트 사례 : ((1,(2,3)),4,(5,6))(1,(2,3),4).
Ørjan Johansen

답변:


17

하스켈 , 169148 바이트

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.([]%)익명의 주요 기능입니다. 예를 들어 묶고 flike를 사용 f "(3,(14,1),4,7)"하면 "(,,,)3((,)14 1)4 7"됩니다.

입력이 Haskell 튜플로 제공되지 않는 이유는 무엇입니까? Haskell은 강력한 형식이므로 튜플의 (1,2)유형은 (Int,Int)1 이고 튜플의 (1,(2,3))유형은 (Int,(Int,Int))입니다. 따라서, 제 1 종류의 튜플을 수용하는 기능은 제 2 종류에 적용될 수 없으며, 특히 임의의 튜플 ( 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:우리가 발견하는 경우 해당 항목, 다음 mn 개의 요소와 y, 가장 최근에 발견 요소를 추가하기 전에 목록을 가지고 N-1 항목을. 우리가 필요로하는이 편리 N-1 , 위해 n요소 튜플과 l>>k같은 목록에 작품 "목록을 연결할 k만큼 여러 번 자체와 y요소가 있습니다" . 따라서이 첫 번째 부분은과 같은 문자열을 생성합니다 "((,,,)".
      • foldl(\r x->x++[' '|x>k,r>k]++r)[x]m요소 연접 m(때문에 앞으로 새로운 항목을 추가하여 역순, m그들은 모두 숫자 인 경우 두 개의 소자 사이에 공간을 추가하는 동안 자신이 역순으로 구성되었다) : [' '|x>k,r>k]우리는 현재의 항목이 있는지 확인 x하고 r사전 식 비교하여 숫자이다 그것들은 ","-숫자가 아닌 경우, 이미 괄호로 묶인 튜플 표현이며 '(' < ','보유합니다.
    • l%('(':r)처음에 패턴 일치 가 실패하면 마지막 줄에 끝납니다 l%r=lex r!!0. 이것은 우리가 숫자를 파싱하고 숫자와 문자열의 나머지를 반환해야 함을 의미합니다. 운 좋게도 lex정확히 수행하는 기능이 있습니다 (숫자뿐만 아니라 다음 유효한 Haskell 토큰을 구문 분석합니다). 그러나 결과 튜플은 목록으로 래핑되므로 목록 !!0의 첫 번째 요소를 얻는 데 사용 됩니다.
  • init.tail.fst.([]%)문자열을 가져와 %빈 목록으로 적용하는 주 함수입니다 . 예를 들어, 입력에 대해서는 수확량을 "(1,2)"적용 하므로 외부 튜플과 브래킷을 제거해야합니다. 튜플의 첫 번째 요소를 가져오고 닫는 브래킷과 여는 것을 제거합니다 .([]%)("((,)1 2)","")fsttailinit

편집 :21 바이트 의 골프에 대한 @ Ørjan Johansen 에게 많은 감사 합니다 !


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)되고 인라인 될 수 있습니다 .
Ørjan Johansen

@Bergi :… 실제로 모든 튜플 유형에 대한 인스턴스를 선언 할 수는 없습니다 . :-) (시도 : show (1,2,3,4,5,6,7,8,9,0,1,2,3,4,5)GHCi ,6에서, 끝에 끝에 추가하고 다시 시도하십시오.)
wchargin

1
6 바이트 더 인라인 향상 :을 사용 m<-y:l하고 오른쪽 대신 왼쪽으로 접고 [x]초기 값으로 사용하십시오. 온라인으로 사용해보십시오!
Ørjan Johansen 님이

1
f익명 일 수 있습니다 : init.tail.fst.([]%).
Ørjan Johansen

11

하스켈, 141 바이트138 바이트 (Ø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
_%[]=[]

f유형이 Exp -> String있습니다.

  • 입력 : Template HaskellExp ression (임의의 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.
Ørjan Johansen

7

하스켈 , 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 바이트

-r플래그의 경우 +2 바이트

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).
Ørjan Johansen

@ ØrjanJohansen 잘 잡았습니다. 아침 식사 후 살펴 보겠습니다.
Jordan

3

자바 스크립트, 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.
Neil

2

수학, 94 바이트

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

인쇄 할 수없는 U+F4A1내장 Function함수를 포함합니다 .

List정수를 가져옵니다 String. 이 허용되지 않을 경우, 이는 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

자바 스크립트 (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

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

자바 스크립트 (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");
  }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.