Давайте складемо дієту Хаскелл


21

У Haskell є кортежі, які можна записати як

(a,b,c)

Однак це лише синтаксичний цукор для

(,,)a b c

Загалом, n кортеж може бути утворений з 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-кортежі, але не порожні кортежі ..? Чи введені порожні кортежі?
повністюлюдський

3
@totallyhuman Не потрібно обробляти порожні кортежі.
Пшеничний майстер

У 5-му тестовому випадку є додаткова,
H.PWiz

2
Також під "числами" ви маєте на увазі "натуральні числа"?
Erik the Outgolfer

2
Пропоновані тестові випадки: ((1,(2,3)),4,(5,6))та (1,(2,3),4).
Ørjan Johansen

Відповіді:


17

Haskell , 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І використовуйте як 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 записів, то mмає n елементів, і 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)",""), тому зовнішній кортеж і дужки потрібно видалити. fstотримує перший елемент кортежу, tailзнімає закриває кронштейн і initвідкриваючий.

Редагувати: Велике спасибі @ Ørjan Johansen за гольф в цілому 21 байт !


1 Насправді тип є (Num t1, Num t) => (t, t1) , але це історія, що відрізняється.

2 Ігнорування поліморфних функцій типу id , які фактично не можуть працювати зі своїм вкладом.


1
Можна було б написати поліморфний функцію , використовуючи клас типів Desugarable, але один мав би заявити про випадках для Intі всіх типів кортежів.
Бергі

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
Покращення вбудовування для ще шести байтів: Використовуйте m<-y:l, складіть ліворуч замість правої та використовуйте [x]як початкове значення. Спробуйте в Інтернеті!
Ørjan Johansen

1
fможе бути анонімним: init.tail.fst.([]%).
Ørjan Johansen

11

Haskell, 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.

  • Вхід: рецесія шаблону HaskellExp (тобто стандартне представлення AST значень Haskell довільного типу - в основному, проаналізований код 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

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]врожайність (,)((,)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).
Ørjan Johansen

@ Ø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

Математика, 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 байт (ця версія займає Listвід Lists / 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``})`)

Займає масив цілих чисел та масивів. Редагувати: Збережено 1 байт, використовуючи s+=замість двох окремих застосувань 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

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");
  }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.