ダイエットハスケルを作ろう


21

Haskellには、次のように記述できるタプルがあります。

(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タプルがないため、形成できません。また、空のタプルについても責任を負いません。

ネストされたタプルは、括弧を使用して形成され、操作の順序をオーバーライドできます。

((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
また、「数字」とは「正の整数」を意味しますか?
エリックアウトゴルファー

2
推奨されるテストケース:((1,(2,3)),4,(5,6))および(1,(2,3),4)
Ørjanヨハンセン

回答:


17

Haskell169 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ように、使用するf "(3,(14,1),4,7)"利回り、"(,,,)3((,)14 1)4 7"

入力がHaskellタプルとして提供されないのはなぜですか?Haskellは強く型付けされているため、タプルに(1,2)はtype (Int,Int)1があり、タプルに(1,(2,3))はtypeがあり(Int,(Int,Int))ます。したがって、第1種のタプルを受け入れる関数を第2種に適用することはできません。特に、任意のタプル2を受け取る関数はありません。

説明:

  • p:k="(,"割り当てるための短い道であるp'('してkには","
  • (%)再帰的な解析および変換関数です。最初の引数は既に解析されたタプルエントリのリストで、2番目の引数は元の文字列の残りです。各呼び出しは、現在の変換されたタプル(角括弧で囲まれた)のタプルと、文字列の残りを返します。
    • l%('(':r)文字列が左角かっこで始まる場合、新しいタプルエントリを解析する必要があります。タプルエントリ
      (y,x:s)<-[]%rを再帰的に適用%して取得しy、残りの文字列を次の文字xと残りの文字列に分割しますs。すでに見つかったエントリの現在のリストに
      m<-y:l新しいエントリを追加し、結果を呼び出しyます。lm
    • 次の文字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個のエントリは、m持っているn個の要素とy、最近見つかった要素を追加する前に、リストを持っているのn-1のエントリを。これは、要素のタプルにn-1 が必要であり、要素の数だけリストをそれ自体と連結する」ようにリストで機能,するので便利です。したがって、この最初の部分はのような文字列を生成します。nl>>kky"((,,,)"
      • foldl(\r x->x++[' '|x>k,r>k]++r)[x]m要素を連結しますm(前に新しいエントリを追加することにより、mそれ自体が逆の順序で構築されたため)、両方の要素が2つの要素の間にスペースのみを追加[' '|x>k,r>k]する場合:現在のエントリxr数値が辞書式に比較される彼らに","-彼らは数字でない場合、彼らはすでに括弧で囲まれたタプルの表現であり、'(' < ','保持しています。
    • l%('(':r)最初のパターン一致が失敗した場合、最後の行になりますl%r=lex r!!0。これは、数値を解析し、数値と文字列の残りを返す必要があることを意味します。幸いなことに、lexまさにそれを行う関数があります(数字だけでなく、次の有効なHaskellトークンを解析します)。ただし、結果のタプルはリストにラップされるため、リスト!!0の最初の要素を取得するために使用します。
  • init.tail.fst.([]%)文字列を受け取り%、空のリストを適用するメイン関数です。たとえば、yieldsを"(1,2)"適用するinputの場合、外側のタプルと角かっこを削除する必要があります。タプルの最初の要素を取得し、閉じ括弧と開き括弧を削除します。([]%)("((,)1 2)","")fsttailinit

編集:合計21バイトのゴルフをしてくれた@ØrjanJohansenに感謝します


1実際、タイプは(Num t1、Num t)=>(t、t1)ですが、それは別の話です。

2 idのようなポリモーフィック関数を無視します。これは、入力では実際には機能しません。


1
typeclassを使用してポリモーフィック関数を作成できますDesugarableが、、Intおよびすべてのタプル型のインスタンスを宣言する必要があります。
ベルギ

1
g短縮しfoldr1(\x r->x++[' '|x>k,r>k]++r)てインライン化できます。
Ørjanヨハンセン


1
さらに6バイトのインライン化の改善:を使用しm<-y:l、右ではなく左に折り[x]、初期値として使用します。オンラインでお試しください!
Ørjanヨハンセン

1
f匿名にすることができますinit.tail.fst.([]%)
Ørjanヨハンセン

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ます。

  • 入力:テンプレートHaskell領域Exp(つまり、任意のタイプの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
2つの場所に変更")"++')':tail括弧の外側に移動することでスペースを節約できます。
Ørjanヨハンセン

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バイト

-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ヨハンセン

@ØrjanJohansen良いキャッチ。朝食を見てみましょう。
ヨルダン

3

JavaScript、75バイト

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

数値の配列|配列、出力文字列。

ニールのおかげで、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]])/@#}<>""&

unprintable 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

JavaScript(ES6)、88 84バイト

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

整数と配列の配列を取ります。編集:s+=の2つの別々の使用の代わりに使用して1バイトを保存しましたs+。内側の3項を単純化できるようになったので、さらに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配列/整数の配列は問題ないはずです。
ジョンファンミン

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");
  }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.