Распечатать канторский набор

19

Соревнование

Построить набор Кантора с N-уровневой структурой .

Троичный набор Кантора создается путем многократного удаления открытых средних третей набора отрезков.

Программа получает один параметр N(целое число) и затем печатает (в консоли или аналогичным образом) набор Кантора из N уровней. Печать может содержать только символы undescore ( _) и пробелы. Параметр может быть положительным или отрицательным, а знак указывает на ориентацию построения набора Кантора: если N > 0набор Кантора построен вниз и если N < 0набор Кантора построен вверх. Если N = 0тогда программа печатает одну строку ( _).

Например:

N = 2

_________
___   ___
_ _   _ _

N = -2

_ _   _ _
___   ___
_________

N = 3

___________________________
_________         _________
___   ___         ___   ___
_ _   _ _         _ _   _ _

N = -3

_ _   _ _         _ _   _ _
___   ___         ___   ___
_________         _________
___________________________

Критерии победы

Так как это сложная игра в гольф, выигрывает самый короткий код.

Отредактировано: изменить 0 ввод по предложению Угорена.

Аверроэс
источник
Почему ничего не печатать, когда N = 0? Это делает 0 особым случаем и усложняет использование рекурсии. Общая обработка будет состоять в том, чтобы напечатать один _(но печатать его вниз при получении -0).
Угорен
Правильно. Я уже модифицировал спецификации.
Аверроэс

Ответы:

10

GolfScript, 49 42 40 символов

~.abs.3\?'_'*\{.3%..,' '*\++}*](0>2*(%n*

Благодаря хаммару за 42-> 40.

Моя лучшая попытка использовать более теоретический подход, к сожалению, гораздо длиннее:

~.abs:^3\?,{3^)?+3base(;1+1?.'_'*^@-)' '*+}%zip\0>2*(%n*

или

~.abs 3\?:^,{6^*+3base.1+1?.('_'*@,@-' '*+}%zip\0>2*(%n*

и я подозреваю, что длина baseи zipсделает невозможным наверстать упущенное.

Питер Тейлор
источник
~.abs.@/\.3\?'_'*\{.3%..,' '*\++}*](%n*39 символов, но вылетает при вводе 0. :-(
Ильмари Каронен
@IlmariKaronen, да, деление на ноль было проблемой для реализации C, которую я тоже написал, потому что это означало, что вы не n/abs(n)можете получить, чтобы получить signum(n).
Питер Тейлор
6

Питон, 116 113 104 103 символов

n=input()
d=n>0 or-1
for i in range(n*d+1)[::d]:
 s='_'*3**i
 while i<n*d:s+=len(s)*' '+s;i+=1
 print s

Более старый алгоритм на 113 символов

r=input()
u='_'
l=[u]
for _ in abs(r)*u:o=len(l[0]);l=[s+o*' '+s for s in l]+[u*o*3]
print'\n'.join(l[::r>0 or-1])
Стивен Румбальский
источник
5

Рубин (97)

Основанный на питонской версии Стивена Румбальски:

n,r=$*[0].to_i,[?_]
n.abs.times{z=r[0].size;r=r.map{|s|s+' '*z+s}+[?_*z*3]}
puts n<0?r:r.reverse

Предыдущие попытки, одинаковой длины (112)

Построить линии из частей:

c=->x,n{n<1??_*x :(z=c[s=x/3,n-1])+' '*s+z}
r=(0..m=(n=$*[0].to_i).abs).map{|i|c[3**m,i]}
puts n<0?r.reverse: r

Начните с одной строки, сделайте в ней отверстия:

r=[?_*3**a=(n=$*[0].to_i).abs]
a.times{|c|r<<r[-1].gsub((x=?_*o=3**(a-c-1))*3,x+' '*o+x)}
puts n<0?r.reverse: r
jsvnm
источник
3

Perl, 93 символа

@x=($t=$x=_ x 3**($a=abs($n=<>)),map$x.=$"x($x=~s/(.)../$1/g).$x,1..$a);say for$n<0?sort@x:@x

Я подумал, что попробую посмотреть, насколько хорошо решение Golf TScript от Peter Taylor будет портировать на Perl. Известные особенности включают использование sortвместо того, reverseчтобы сохранять три символа, используя тот факт, что пространство сортируется раньше _.

Илмари Каронен
источник
2

Common Lisp, 217 210 символов

(defun m(x)(flet((c(n v)(if(= n 0)`((,v))(cons(substitute v nil(make-list(expt 3 n)))(mapcar #'append(c(1- n)v)(c(1- n)" ")(c(1- n)v))))))(format t "~{~{~a~}~%~}"(let((r(c(abs x)'_)))(if(< x 1)(reverse r)r)))))

Expanded:

(defun m(x)
  (flet((c(n v)
    (if(= n 0)
       `((,v))
       (cons(substitute v nil(make-list(expt 3 n)))
            (mapcar #'append
                    (c(1- n)v)
                    (c(1- n)" ")
                    (c(1- n)v))))))
   (format t "~{~{~a~}~%~}"(let((r(c(abs x)'_)))(if(< x 1)(reverse r)r)))))

Я полагаю, если лисповскому коду удается превзойти любое начальное значение для другого языка (C, 219), я в порядке :)

Пол Рихтер
источник
2

С ( 163 161 символ)

i,l,N;f(n,m,s){if(n){s=--n<l?m:s;f(n,m,s);f(n,s,s);f(n,m,s);}else
putchar(m);}main(n,v)int**v;{for(i=N=abs(n=atoi(1[v]));i+1;i--)l=n<N?N-i:i,f(N,95,32),f(0,10);}

Занимает пару трюков из ответа Угорена , но основная логика совершенно иная. Я не мог следить за его циклом for, так что можно было бы гибридизировать и сохранить еще несколько.

Питер Тейлор
источник
2

C 219 193 179 143 136 131 символов

Следуя еще одной из идей Петьера Тейлора, плюс мое собственное улучшение, спасло еще 6.
Интегрировал несколько советов от @PeterTaylor, а также скопировал его основную функцию с небольшими изменениями, которые сохраняют персонажа (справедливо ли копировать его? Поскольку никто из нас не выиграет этот, я думаю, это не так уж плохо).
Я подумал о значительном улучшении в том, как работает моя рекурсия, и, увидев ответ Питера Тейлора, я реализовал его, чтобы вернуть себе лидерство. Когда я снова прочитал его ответ, я увидел, что сделал почти то же, что и он. Так что это похоже на гибридизацию, которую он предложил.
Также упростили цикл main, сохранив одинаковую длину.
И взял у Питера трюк, чтобы напечатать перевод строки, а не puts("")- спасает персонажа.

Удалено intиз объявления переменной - предупреждение, но сохраняет 4 символа.
Новый алгоритм не рассчитывает 3 ^ x заранее, но использует один цикл для печати 3 ^ x символов.
Можно сохранить еще один, определив int*v, но тогда 64-битный не будет работать.
Количество символов исключает пробелы (которые могут быть удалены).

o,i,n;
p(c) {
    n-- ?
        p(c),p(o>n?c:32),p(c)
    :
        putchar(c);
    n++;
}
main(c,v)int**v; {
    for(n=abs(c=atoi(v[1]));i<=n;i++)o=c+n?n-i:i,p(95),puts("");
}

Старый алгоритм, 219 символов:

p(l,o,i,m,c,j) {
    for(;i<(m=l);i++)
        for(j=0,c=95;m/o||!putchar(c);j++)
            i/m%3-1||(c=32),m/=3;
    puts("");
}
main(c,v,n,i,l,o)int**v;{
    (n=atoi(v[1]))<0?n=-n:(c=0);
    for(i=n,l=1;i;i--)l*=3;
    o=c?1:l;
    for (;i<=n;i++)p(l,o,0),c?o*=3:(o/=3);
}
ugoren
источник
@PeterTaylor, я не могу удалить iпараметр, потому что использование global может помешать main. l--будет мешать o>=l, и мне придется заменить его >(так почему я пишу это, как будто это плохо?) Я мог бы также скопировать вас main, который проще и короче, чем мой.
Угорен
@PeterTaylor, вы были правы i- я упустил тот факт, что я действительно больше не использую его (я думал, что вы имеете в виду, я не передаю это).
Угорен
Кстати, я не против, чтобы ты взял мою основную функцию. Мое эмпирическое правило заключается в том, что копирование чужого решения для изменения одного символа является чрезмерно агрессивным, копирование чужого решения для перезаписи его половины совершенно справедливо, и где-то между ними есть серая область. Возможно, нам следует попытаться согласовать некоторые стандарты сообщества по мета.
Питер Тейлор
@PeterTaylor, я думаю, что мы зашли в тупик. Мой pтеперь кажется вполне оптимальным, и вы mainстали лучше (я не уверен, что он оптимален, но не могу улучшить его дальше). Таким образом, за исключением новой гениальной структуры программы, единственным путем было копирование кода другого.
Угорен
Кстати, как вы считаете своих персонажей? Потому что я делаю твою последнюю версию 138 символов, а не 136.
Питер Тейлор
2

J 44 39 38 37 байт

' _'{~0&>_&(]|.)(,:1)1&(,],.0&*,.])~|

Использует итерацию для построения следующего набора, начиная с 1 (представляющего _) изначально.

использование

   f =: ' _'{~0&>_&(]|.)(,:1)1&(,],.0&*,.])~|
   f 0
_
   f 1
___
_ _
   f _1
_ _
___
   f 2
_________
___   ___
_ _   _ _
   f _2
_ _   _ _
___   ___
_________
   f 3
___________________________
_________         _________
___   ___         ___   ___
_ _   _ _         _ _   _ _
   f _3
_ _   _ _         _ _   _ _
___   ___         ___   ___
_________         _________
___________________________

объяснение

' _'{~0&>_&(]|.)(,:1)1&(,],.0&*,.])~|  Input: integer n
                                    |  Absolute value of n
                (,:1)                  The array [1]
                     1&(          )~   Repeat abs(n) times starting with x = [1]
                                 ]       Identity function, gets x
                            0&*          Multiply x by 0
                               ,.        Join the rows together
                         ]               Identity function, gets x
                          ,.             Join the rows together
                     1  ,                Prepend a row of 1's and return
      0&>                              Test if n is negative, 1 if true else 0
         _&(   )                       If n is negative
             |.                          Reverse the previous result
            ]                            Return that
                                       Else pass the previous result unmodified
' _'                                   The string ' _'
    {~                                 Select from the string using the result
                                       as indices and return
миль
источник
Ницца! Я лично не пробовал, но мне нравится использовать повестку дня - @.возможно, это, в сочетании с $:, может быть полезным здесь? Например, что-то вроде (zero case)`(positive case)`(negative case)@.*, или даже, может быть ":@_:`(positive case)`(|."1@$:)@.*.
Конор О'Брайен
Я не пытался рекурсивного решения, но я мог попробовать это.
миль
2

R , 141 139 137 байт

m=abs(n<-scan());write("if"(n<m,rev,c)(c(" ","_")[Reduce(`%x%`,rep(list(matrix(c(1,1,1,1,0,1),3)),m),t(1))[,1+2^m-2^(m:0)]+1]),1,3^m,,"")

Попробуйте онлайн!

-15 байтов, спасибо Джузеппе за использование в '('качестве функции идентификации; writeвместо того, catчтобы печатать вывод; умное использование %x%.

-2 байта благодаря Кириллу Л. с использованием cвместо '('функции идентичности.

Jayce
источник
может ли продукт Kronecker работать здесь? %x%? Возможно, есть некоторые проблемы с чередованием строк ...
Джузеппе
@Giuseppe Я попытался, основываясь на вашем ответе «Создать букву« H »из меньших букв« H »... Я попробую еще раз».
JayCe
Ах, так это ты проголосовал за это. это единственная причина, о которой я kronтоже подумал ! Я полагаю, что это может уменьшиться до 125 байтов, если мы сможем найти правильный подход.
Джузеппе
Вы можете использовать `(`в качестве функции идентификации, так что вы можете использовать writeнепосредственно вместо catи forцикла. 141 байт
Джузеппе
@ Giuseppe Я понятия не имел, (можно было бы использовать таким образом, или это if можно использовать для выбора из двух функций. И я начну использовать write ... сохраняет много "\ n".
JayCe
1

Python, 177 164 символа

N=input()
n=abs(N)
c=lambda x:0if x<1 else x%3==1or c(x/3)
r=["".join([["_"," "][c(x/3**i)]for x in range(3**n)])for i in range(n+1)]
print"\n".join(r[::N>0 or-1])
ставка
источник
Поскольку вы используете Python 2, вам не нужно приводить результаты inputкак int. Ваши последние две строки могут быть сокращены доprint"\n".join(r[::N>0 or-1])
Стивен Румбальски
@ Стивен я внес изменения. Спасибо.
Ante
1

Perl, 113 символов

$i=abs($I=<>);@w=$_='_'x3**$i;while($i--){$x=3**$i;s/(__){$x}/'_'x$x.' 'x$x/eg;push@w,$_}say for$I>0?reverse@w:@w

Expanded:

$i=abs($I=<>);
@w=$_='_'x3**$i;
while($i--){
    $x=3**$i;
    s/(__){$x}/'_'x$x.' 'x$x/eg;
    push@w,$_
}
say for$I>0?reverse@w:@w
Toto
источник
1

JavaScript 121 байт

Внутренняя рекурсивная функция, а затем позаботиться о обратном выводе

n=>(f=(n,t=n&&f(n-1),r=t[0])=>n?[r+r+r,...t.map(x=>x+t[n]+x)]:['_',' '],f=f(n<0?-n:n),f.pop(),n<0?f.reverse():f).join`\n`

Меньше гольфа

n=>{
  var f = n => { // recursive function
    var t = n && f(n-1), r = t[0]
    return n 
      ? [r+r+r, ...t.map(x => x+t[n]+x)]
      : ['_',' ']
  };
  f = f(n < 0 ? -n : n);
  f.pop(); // last row is all blanks
  if (n<0) f.reverse();
  return f.join`\n`
}

Тестовое задание

var F=
n=>(f=(n,t=n&&f(n-1),r=t[0])=>n?[r+r+r,...t.map(x=>x+t[n]+x)]:['_',' '],f=f(n<0?-n:n),f.pop(),n<0?f.reverse():f).join`\n`

function go()
{
  var n=+I.value
  O.textContent = F(n)
}

go()
<input id=I type=number value=3 oninput='go()'>
<pre id=O></pre>

edc65
источник
1

Пакет, 265 262 242 236 235 байт

@echo off
set/pn=
set c=%n%,-1,0
if %n% lss 0 set c=0,1,%n:-=%
for /l %%i in (%c%)do call:l %%i
exit/b
:l
set s=_
for /l %%j in (1,1,%n:-=%)do call:m %1 %%j
echo %s%
:m
set t=%s%
if %1 lss +%2 set t=%s:_= %
set s=%s%%t%%s%

Изменить: Сохранено 12 19 байт благодаря @ l4m2. Сохранено 8 байтов путем удаления ненужной %a%переменной.

Нил
источник
Это для 247 байтов.
Конор О'Брайен
@ ConorO'Brien Имейте в виду, что было бы 261, если бы я посчитал все CR, а также LF (что я уверен, что вы не обязаны делать, но я ленив, как это).
Нил
То есть вы не удаляете CR из своего кода? Даже если это не требуется .BAT-файлам и все равно удаляется SE? : P
Конор О'Брайен
@ ConorO'Brien Я согласен на использование Блокнота для записи пакетных файлов.
Нил
Вы можете сделать что-то подобное set c=%n%,-1,0 [LF] if %n% lss 0 set c=0,1,%a% [LF] for /l %%i in (%c%)do call:l %%i?
м2
0

PowerShell , 111 байт

filter f{if($s=[math]::Sign($_)){($x=$_-$s|f|%{$_+' '*($l=$_|% Le*)+$_})|?{$s-1};'_'*3*$l;$x|?{$s+1}}else{'_'}}

Попробуйте онлайн!

Менее гольф:

filter f{
    if($sign=[math]::Sign($_)){
        $x=$_-$sign|f|%{
            $_+' '*($length=$_|% Length)+$_
        }
        $x|?{$sign-1}  # output $x if $_ is negative
        '_'*3*$length
        $x|?{$sign+1}  # output $x if $_ is positive
    }
    else{
        '_'
    }
}
Mazzy
источник