Таблица умножения в одну строку


    На картинке вы видите обычную таблицу умножения, которая, думаю, всем хорошо знакома.
    Ничего особенного в ней нет, кроме того, что весь алгоритм ее построения сжат до одной стандартной Python’овской строки в 79 символов (см. PEP8). Кому интересно добро пожаловать под кат.

    Для начала, стоит ответить на появившийся у многих вопрос «А зачем это?». Все сложилось из двух факторов, во-первых я, по настоятельной рекомендации одного хорошего человека, занялся углубленным изучением Python’а, а во-вторых мне нравится концепция демосцены. Результатом стало желание написать (конечно слишком громко сказано) что-нибудь очень маленькое (в идеале в одну строку), но наглядное, используя для всего этого особенности кодинга на Python’е. Я решил вывести на экран таблицу умножения.

    Стоит отметить, что пишу я на Python3.7. Версии младше 3.6 не подойдут из-за отсутствия поддержки f-строк, и в итоге рабочий скрипт превысит длину в 79 символов.

    Как восемь строк превратились в одну


    Для начала я написал код выводящий таблицу умножения, абсолютно не заботясь о компактности:



    Листинг
    def MultiTabl():
    	tabl = ''
    	for y in range(1,11):
    		for x in range(1,11):
    			tabl += f'{x*y}\t'
    		tabl += f'\n'
    	return tabl
    


    Сформировать значения таблицы можно, используя генераторы, а циклы оставить для распаковки списков. Минусом такого подхода оказалось большее количество строк:



    Листинг
    def MultiTabl():
    	nums = [[x*y for x in range(1,11)] for y in range(1,11)]
    	tabl = ''
    	for line in nums:
    		for item in line:
    			tabl += f'{item}\t'
    		tabl += f'\n'
    	return tabl
    


    Генератору можно отдать и расстановку Tab’ов (‘\t’) используя f-строки:

    nums = [[f'{x*y}\t' for x in range(1,11)] for y in range(1,11)]
    

    Если извлеченный в первом цикле список склеить в строку, с помощью строкового метода join(), использовать параллельное назначение переменных и разместить цикл в одной строке, то размеры кода значительно уменьшатся:



    Листинг
    def MultiTabl():
    	nums, tabl = [[f'{x*y}\t' for x in range(1,11)] for y in range(1,11)], ''
    	for line in nums: tabl += ''.join(line) + '\n'
    	return tabl
    


    А если внести join() и ‘\n’ в генератор:

    def MultiTabl():
    	nums, tabl = [''.join([f'{x*y}\t' for x in range(1,11)])+'\n' for y in range(1,11)], ''
    	for line in nums: tabl += line
    	return tabl
    

    Теперь в нашем распоряжении список из строк, и его тоже можно склеить с помощью join(), избавившись тем самым от циклов:
    def MultiTabl():
    	tabl = ''.join([''.join([f'{x*y}\t' for x in range(1,11)])+'\n' for y in range(1,11)])
    	return tabl
    

    Ну и обещанный вариант в одну строку (от print, конечно, не избавиться)

    print(
    ''.join([''.join([f'{x*y}\t' for x in range(1,11)])+'\n' for y in range(1,11)])
    )
    

    Конечно, гуру Python’а скажут “И что тут такого?”, но стоит заметить, что указанный подход не является очевидным для начинающих.

    Не стоит воспринимать сей опус слишком серьезно, я его писал как разминку перед большой статьей, будет замечательно если он вызовет улыбку, и просто великолепно если принесет пользу.

    Similar posts

    Ads
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More

    Comments 53

      +8
      Конечно, гуру Python’а скажут “И что тут такого?”, но стоит заметить, что указанный подход не является очевидным для начинающих.

      Начинающим такой подход противопоказан.

        +10
        Создадим язык, который требует использовать отступы для сохранения понятной структуры, а потом будем пилить в нем однострочные программы, к которым требуется страница комментариев.
          +5
          Это скорее «Code golf», жаль на Хабре нет такой дисциплины :)
          j=''.join;r=range(1,11);print(j(j(f'{x*y}\t' for x in r)+'\n' for y in r))

          Вариант в одну строку с print
          UPD: Выкинул квадратные скобки
            –2
            Не знал про такое направление. И да, по сути это получается «Code golf». ))
              –2
              Ваше Кунг-Фу сильнее )))
                +1

                С использованием точки с запятой неинтересно

                  –2
                  Вот вариант без точки с запятой:
                  (основано на лучшем пока решении habr.com/ru/post/458362/#comment_20345872)
                  r,_=range(1,11),[print(*[x*y for x in r],sep='\t')for y in r]
                  

                  Интересно? :)
                    +1

                    Да, но не работает, r is not defined :)

                      0
                      Да, что-то я забыл сбросить переменные:
                      _=[print(*[x*y for x in range(1,11)],sep='\t')for y in range(1,11)]
                      

                      Да, не так интересно…
                +2
                Это больше для тренировки мозгов, чем для практического применения. Прошли те времена, когда на Спектруме плавно гасили экран кодом из 21 байта.
                  +3
                  r=range(1,11);print('\n'.join('\t'.join(f'{x*y}'for x in r)for y in r))
                  71

                  во 2м питоне можно 89, так как скобочки у принта не нужны:
                  r=range(1,11);print'\n'.join('\t'.join(str(x*y)for x in r)for y in r)
                    +1
                    Самое короткое, конечно, в байтах (то есть в две строки):
                    r=range(1,11)
                    for y in r:print('\t'.join(str(x*y)for x in r))
                    62 байта в p3, 60 в p2
                      +1
                      О, ванлайнер в 65 для p3 (не для p2):
                      r=range(1,11);_=[print('\t'.join(str(x*y)for x in r))for y in r]

                      [63, если допустить непустой результат, как ниже GCU сделал]:
                      r=range(1,11);[print('\t'.join(str(x*y)for x in r))for y in r]
                        0
                        Ваше Кунг-Фу сильнее )))
                    +1
                    Можно в одну строку и с print
                    _=[print(*[str(x*y) for x in range(1,11)],sep='\t') for y in range(1,11)]
                    

                      +1
                      Прикольно получилось c распаковкой и переводом строки от print. Можно ещё чуток укоротить:
                      r=range(1,11);[print(*[str(x*y)for x in r],sep='\t')for y in r]
                        +3
                        Преобразование в строку оказалось не нужным
                        r=range(1,11);[print(*[x*y for x in r],sep='\t')for y in r]

                        59
                          0
                          Скобок можно ещё меньше
                          r=range(1,11);for y in r:print(*(x*y for x in r),sep='\t')
                          

                          58
                      0
                      JS
                      for(s='',i=11;i++<121;s+=i%11?'\t'+(i/11|0)*(i%11):'\n');console.log(s);
                        0
                        Ещё на символ короче, итого 71
                        for(s='',i=11;i++<121;s+=(r=i%11)?'\t'+(i/11|0)*r:'\n');console.log(s);

                        И кажется подсветка не осилила
                          0
                          Всё ещё 71, но немного извратнее
                          for(s='',l=i=11;i++-l*l;s+=(r=i%l)?'\t'+(i/l|0)*r:'\n');console.log(s);
                            +1
                            Точку с запятой в конце строки можно опустить. Минус 1! =)
                              +1
                              for(s='',l=i=11;i++-l*l;s+=i%l?'\t'+i%l*(i/l|0):'\n');console.log(s)

                              на 2 символа меньше (без учёта точки с запятой)

                                0
                                Блин, я всё пытался так сделать, но не получалось
                                  0
                                  Если писать код в консоли, то вот так тоже работает
                                  for(s='',l=i=11;i++-l*l;s+=i%l?'\t'+i%l*(i/l|0):'\n');s 

                                  image
                                    0

                                    но не в терминале node


                                    > for(s='',l=i=11;i++<l*l;s+=i%l?'\t'+i%l*(i/l|0):'\n');s
                                    
                                    '\t1\t2\t3\t4\t5\t6\t7\t8\t9\t10\n\t2\t4\t6\t8\t10\t12\t14\t16\t18\t20\n\t3\t6\t9\t12\t15\t18\t21\t24\t27\t30\n\t4\t8\t12\t16\t20\t24\t28\t32\t36\t40\n\t5\t10\t15\t20\t25\t30\t35\t40\t45\t50\n\t6\t12\t18\t24\t30\t36\t42\t48\t54\t60\n\t7\t14\t21\t28\t35\t42\t49\t56\t63\t70\n\t8\t16\t24\t32\t40\t48\t56\t64\t72\t80\n\t9\t18\t27\t36\t45\t54\t63\t72\t81\t90\n\t10\t20\t30\t40\t50\t60\t70\t80\t90\t100\n'
                                0
                                Меняем порядок операндов, убираем точку с запятой в конце. Получается 69 байт

                                for(s='',i=11;i++<121;s+=i%11?'\t'+i%11*(i/11|0):'\n');console.log(s)
                                  0

                                  выше отправил на 68 символов

                              0

                              как гуру питона я такой код реждекчу на code review. Причина — я ничерта не понимаю что хочет автор. Что делает — понимаю, а что хочет — нет. И это не одно и то же, к сожалению. Если бы было одно и то же, то у нас бы не было багов.


                              А баг возникнет в тот момент, когда кто-то заменит 11 на чуть большую константу.


                              >>> print (''.join([''.join([f'{x*y}\t' for x in range(999999, 1000010)])+'\n' for y in range(9,12)]))
                              8999991 9000000 9000009 9000018 9000027 9000036 9000045 9000054 9000063 9000072 9000081 
                              9999990 10000000    10000010    10000020    10000030    10000040    10000050    10000060    10000070    10000080    10000090    
                              10999989    11000000    11000011    11000022    11000033    11000044    11000055    11000066    11000077    11000088    11000099

                              Вы уверены, что это таблица умножения?

                                +4
                                За попытку протащить кодегольф в прод следует лишение пальцев рук линейками в фигуральном смысле.
                                  0
                                  Мой вариант на JS вполне гибок, до скольки угодно строит таблицу, и читаем гораздо хуже. Возьмёте?
                                    0
                                    в прод? без валидаций? :)
                                    при for(s='',l=i=11.1;i++-l*l;s+=(r=i%l)?'\t'+(i/l|0)*r:'\n');console.log(s);
                                    вкладка съест 2 Гб за минуту и упадет
                                      +1
                                      А вы не делайте так, и не упадёт :)
                                  0
                                  from itertools import starmap, product
                                  from operator import mul
                                  
                                  for i in range(1, 11): print(*starmap(mul, product(range(1, 11), [i])), sep="\t")

                                  Длиннее, зато питонистее :)

                                    +1

                                    Не мог пройти мимо


                                    julia> [x*y for x in 1:10, y in 1:10]
                                    10?10 Array{Int64,2}:
                                      1   2   3   4   5   6   7   8   9   10
                                      2   4   6   8  10  12  14  16  18   20
                                      3   6   9  12  15  18  21  24  27   30
                                      4   8  12  16  20  24  28  32  36   40
                                      5  10  15  20  25  30  35  40  45   50
                                      6  12  18  24  30  36  42  48  54   60
                                      7  14  21  28  35  42  49  56  63   70
                                      8  16  24  32  40  48  56  64  72   80
                                      9  18  27  36  45  54  63  72  81   90
                                     10  20  30  40  50  60  70  80  90  100
                                    
                                    julia> "[x*y for x in 1:10, y in 1:10]"|>length
                                    30

                                    или юзая линал (транспонирование столбца в строку):


                                    [1:10...].*[1:10...]'

                                    Ладно, то что интерпретатор выводит результат не честно, тогда


                                    ["$(prod(["$(x*y) " for x=1:10]))\n" for y=1:10]|>prod|>print

                                    61 символ

                                      0

                                      59 символов с использованием анонимных функций и 55 с если можно брать точку с запятой


                                      prod(y->prod(x->"$(x*y) ",[1:10...])*'\n',[1:10...])|>print
                                      X=[1:10...];prod(y->prod(x->"$(x*y) ",X)*'\n',X)|>print
                                        0

                                        [1:10;] еще так можно

                                        +1
                                        Matlab:
                                        [1:10]'*[1:10]
                                        0
                                        Больше 60 символов — уже не одна строка, на любом си-подобном языке можно написать какую угодно программу в одну строку…
                                          +1

                                          Откуда взялось магическое число 60?

                                            0
                                            Из наших соглашений по оформлению кода (по крайней мере в некоторых, которые видел — ограничивают длину строки кода в 60 символов).
                                              0
                                              Имхо это карго культ. Часто такие строки становятся совершенно нечитаемы. Это имело смысл во времена когда в терминал влазило 60 символов. Сегодня нужно либо договориться об общем ограничении команды, либо вообще забить на это и множество других правил и работать не изображая формализм и веря что формализм приведёт нас к решению поставленных задач. Технические решения человеческих проблем не решают.
                                                0
                                                Ну для этого и составляются соглашения по оформлению… Правда сейчас погуглил примеры, там везде (где вообще указано ограничение) — 120 символов
                                                0

                                                Даже в PEP-8 указано 79 символов. Всё что меньше — это уже мазохизм какой-то

                                            +2
                                            Удивительны дела твои, Хабр: людям понравилось упражняться в сжимании кода в одну строчку, а статью все равно минусуют!
                                              +1
                                              Главное, что понравилось ))
                                              0
                                              Challange accepted:
                                              Debug.Log(Enumerable.Range(0, 100)
                                                  .Select(i => (i / 10 + 1) * (i % 10 + 1) + ((i + 1) % 10 == 0 ? "\n" : "\t"))
                                                  .Aggregate("", (a, b) => a + b)
                                              );
                                                0
                                                Чуть короче
                                                Debug.Log(
                                                string.Concat(new int[100].Select((v,i)=>(i/10+1)*(i%10+1)+((i+1)%10==0?"\n":"\t")))
                                                );
                                                +3
                                                Хотя бы раз попробую оправдать свой ник и поучаствую в челендже
                                                ++++++++++[>++++++++++<-]>[->+>[-]>[-]++++++++++<<[->+>-[>+>>]>[+[-<+>]>+>>]<<<<<<]>[<+>-]>[-]>[<+<+>>-]+<[>-<[-]]>[>-<<<++++++++++>>-]<<[>+>+<<-]>>>+<[>[>+>+<<-]>[<+>-]<<-]>[-]>>[>>+>+<<<-]>>>[<<<+>>>-]<<+>[<->[>++++++++++<[->-[>+>>]>[+[-<+>]>+>>]<<<<<]>[-]++++++++[<++++++>-]>[<<+>>-]>[<<+>>-]<<]>]<[->>++++++++[<++++++>-]]<[.[-]<]<[-]<<<<---------->+<[>-<[-]+++++++++.[-]]>[[-]++++++++++.[-]]<<<<]
                                                P.S. Ужимать код не пытался, главное, что вообще работает.
                                                  0

                                                  Мощно! Можно для сжатия сделать обертку, что-то типа +10[>+10<-]… да и раз алфавит такой короткий, можно использовать, скажем, Int16

                                                +2
                                                Как раз учу Питон и вспомнил топик. Если красота неважна, можно в 60 символов
                                                import numpy;x=range(1,11);print(numpy.multiply.outer(x, x))
                                                  0
                                                  _=[print('\t'.join(map(str,range(x,x*11,x)))) for x in range(1,11)]

                                                  Only users with full accounts can post comments. Log in, please.