Реализация «tac»: печатать строки из файла в обратном порядке


30

Между вопросом о котенке и появлением этого вопроса в U & L о какой-то sedмагии, как насчет реализации tac?


Задача

Реализуйте программу, которая перевернет и напечатает строки в файле.


вход

Файл, предоставленный как имя или через стандартный ввод


Выход

Линии, перевернутые, чтобы выровнять.


счет

Байты исходного кода.


9
tacнемного странно, когда речь заходит о переводе строки. Он преобразуется a\nb\n(завершающий перевод строки) в b\na\nи a\nb(без завершающего перевода строки) в ba\n. Это то, как наш код должен себя вести?
Деннис


10
Кроме того, если нам нужно повторить поведение tac, то 3-байтовые ответы Bash, которые выполняются, tacявляются лишь вопросом времени ...
Деннис

1
@ Денис на данный момент, вероятно, лучше оставить неопределенным.
Ник Т

1
@ Денис имеет смысл для меня. Визуализируйте строки файла как горизонтальные строки, все заканчивающиеся на \n. tacменяет порядок этих строк Если an \nудален из середины файла, строка, которую он завершил, присоединяется к следующей строке, но в случае последней строки следующей строки нет, к которой можно присоединиться.
Blacklight Shining

Ответы:


15

GS2, 3 байта

* +

Три байта - это по порядку разделенные линии, обратные и соединительные строки.


9

Perl, 11 байт

$\=$_.$\}{

Ведет себя точно так же, как tac. Для этого кода требуется -pпереключатель, который я посчитал 1 байтом.

Тестовые прогоны

$ echo -en 'a\nb' | perl -pe'$\=$_.$\}{' | xxd -g 1
0000000: 62 61 0a                                         ba.
$ echo -en 'a\nb\n' | perl -pe'$\=$_.$\}{' | xxd -g 1
0000000: 62 0a 61 0a                                      b.a.

Как это работает

Как объясняется здесь , -pпереключатель в основном while (<>) { ... ; print }охватывает программу, поэтому исходный код эквивалентен

 while(<>)
 {
   $\ = $_ . $\
 }
 print

Для каждой строки ввода мы добавляем текущую строку ( $_) к $\(изначально неопределенному), обновляя последнюю с результатом.

После обработки всех строк printпечатается значение локальной переменной $_(не определено в этой области), за которым следует разделитель выходных записей ( $\).


Не хочешь объяснить, как это работает?
xebtl

2
@ xebtl Evilly. Добавление -pпереключателя оборачивает ваш код в цикл, который начинается while(<>){и заканчивается } continue { print }, что позволяет фильтровать входные данные просто путем изменения $_. $\=$_.$\добавляет каждую строку ввода к разделителю выходной записи, и преждевременно }{завершает поставляемый perl whileблок, поэтому continueблок больше не присоединяется к нему. Таким образом, все строки ввода добавляются $\в обратном порядке, а затем, в конце continue { print }концов, запускается, печатая «ничто» ( $_будет не определено после конца ввода), но с разделителем $\.
Хоббс

@xebtl grr, форматирование кода в комментариях кажется немного нарушенным, когда обратные косые черты и обратные косые черты приближаются друг к другу. Возможно, вы можете догадаться о том, что я пытался сказать.
Хоббс

1
@primo Первый пример показывает, что происходит в этом случае. Вывод будет странным, но в точности как у ТАСа.
Деннис

1
@Dennis страницы 18 ff этой книги
msh210

8

Pyth, 4 байта

j_.z

.zявляется вводом, разделенным строками в виде списка, _переворачивает его и jсоединяет его символом, который по умолчанию является \n.



7

Сетчатка , 7 байт

!rm`.*$

Retina работает в режиме Match с одним регулярным выражением. Обычно это просто печатает количество совпадений, но при этом !мы настраиваем его для печати реальных совпадений (разделенных переводом строки).

Фактическое регулярное выражение просто .*$. .*соответствует любой строке (потенциально пустой), потому что .может соответствовать любому символу, кроме перевода строки. Я вернусь $через минуту.

Как мы заставим его печатать спички в обратном порядке? Используя .NET режим сопоставления справа налево, активируется с помощью r. Это означает, что механизм регулярных выражений запускается в конце строки при поиске совпадений и работает в обратном направлении.

И, наконец, mделает $матч конец строки , а не в конце строки. Зачем нам это вообще нужно? Беда в том, что .*генерирует посторонние совпадения. Рассмотрим подстановку регулярных выражений

s/a*/$0x/

применяется к входу baaababaa. Вы могли бы подумать, что это даст baaaxbaxbaax, но на самом деле это дает вам baaaxxbaxxbaaxx. Зачем? Потому что после сопоставления aaaкурсор двигателя находится между aи b. Теперь он не может больше соответствовать as, но a*также удовлетворен пустой строкой. Это означает, что после каждого совпадения вы получаете еще одно пустое совпадение.

Мы не хотим этого здесь, потому что это введет дополнительные пустые строки, поэтому мы отбрасываем эти посторонние совпадения (которые находятся в начале строк, из-за режима справа налево), требуя, чтобы совпадения включали конец линия.


6

Haskell, 34 байта

main=interact$concat.reverse.lines

[редактировать]

Сохраняется один байт путем замены unlinesна concat.


4

CJam, 7 байтов

qN/W%N*

Читает стандартный вывод, выводит на стандартный вывод.

Объяснение:

q       Get input.
N/      Split at newlines.
W%      Reverse list.
N*      Join with newlines.


4

Befunge-93, 17 байт

~:1+!#v_
>:#,_@>$

Здесь нет ничего особенного; просто положите все в стек, а затем вытолкните.


4

Pure Bash (без внешних утилит), 56

mapfile a
for((i=${#a[@]};i--;));{
printf %s "${a[i]}"
}

Это один из немногих ответов для точной tacэмуляции, о чем спрашивается в комментарии Денниса :

$ echo -en 'a\nb' | ./tacemu.sh | xxd -g 1
0000000: 62 61 0a                                         ba.
$ echo -en 'a\nb\n' | ./tacemu.sh | xxd -g 1
0000000: 62 0a 61 0a                                      b.a.
$ 

Приятно и вдохновляюще .
manatwork


4

JavaScript (SpiderMonkey Shell), 38 байт

[...read(readline())].reverse().join``

Довольно просто


read() читает файл

readline() читает строку из STDIN

[...str]разделит str на массив символов

reverse() обратит массив

join`` объединит массив в строку


4

Python 2, 52 байта

import sys;print''.join(sys.stdin.readlines()[::-1])

1
Разве input () не читает ни одной строки из stdin?
Линн

@Mauris отредактировал это
бета-распад

Как насчет import sys;print sys.stdin.read()[::-1]?
Дитер

@dieter Это меняет каждого персонажа, вызов требует только линий, которые будут изменены
Beta Decay

хорошо, мой плохой - не читал внимательно, извините
Дитер

4

C #, 179 171 байт

using B=System.Console;class A{static void Main(){var a=new System.Collections.Stack();string b;while((b=B.ReadLine())!=null)a.Push(b);foreach(var c in a)B.WriteLine(c);}}

Читает строки, помещая их в стек, а затем записывает их назад. Я бы использовал Mathematica для этого, но он не имеет смысла EOF.


3

sed, 9 байт

1!G;h;$!d

Никакого upvote не хотел, это знаменитый сед-лайнер.


10
Если это не ваша собственная работа, я предлагаю сделать ваш ответ вики-сообществом.
lirtosiast


3

Powershell, 41 байт

$a=$args|%{gc $_};[array]::Reverse($a);$a

Сохраняет содержимое файла построчно a, переворачивает aи, наконец, печатает его.



3

Бурлеск , 6 байтов

ln<-uN

lnразбивает линии, <-переворачивает, uNобъединяет строки и форматы для необработанного вывода.


3

Баш, 48 43 персонажа

(Вдохновленный ответом Digital Trauma 's Bash . Ответы за идею должны пойти к нему.)

mapfile -c1 -C's=$2$s;set'
printf %s "$2$s"

Образец прогона:

bash-4.3$ echo -en 'a\nb' | bash tac.sh | xxd -g 1
0000000: 62 61 0a                                         ba.

bash-4.3$ echo -en 'a\nb\n' | bash tac.sh | xxd -g 1
0000000: 62 0a 61 0a                                      b.a.

Я думаю, что вы можете сделать mapfile -c1 -Cfвместо mapfile -c1 -Cf a.
Цифровая травма

Правильный. Я также обнаружил это в то же время, просто -Cсначала попробовал что-то непростое .
manatwork

3

GNU Awk, 27 символов

(Вдохновленный Ed Мортон «s GNU Awk ответ . CW , как я не намеревался похитить его решение.)

{s=$0RT s}END{printf"%s",s}

Обратите внимание, что при изменении RTRSэто становится переносимым стандартным Awk, но теряет способность сохранять отсутствие последнего перевода строки.

Образец прогона:

bash-4.3$ echo -en 'a\nb' | awk '{s=$0RT s}END{printf"%s",s}' | xxd -g 1
0000000: 62 61 0a                                         ba.

bash-4.3$ echo -en 'a\nb\n' | awk '{s=$0RT s}END{printf"%s",s}' | xxd -g 1
0000000: 62 0a 61 0a                                      b.a.

Вы можете удалить "% s"
ниндзя

@ninjalj, только если мы можем предположить, что ввод никогда не будет содержать «%».
manatwork


2

Гема, 25 знаков

*\n=@set{s;$0${s;}}
\Z=$s

Образец прогона:

bash-4.3$ echo -en 'a\nb' | gema '*\n=@set{s;$0${s;}};\Z=$s'
ba

bash-4.3$ echo -en 'a\nb\n' | gema '*\n=@set{s;$0${s;}};\Z=$s'
b
a


2

sed, 7 байт

G;h;$!d

Это работает для меня (и это самое короткое решение в других местах), но я действительно не хочу выяснять, почему. Я просто возился со знаменитым 9-байтовым трюком, пока не нашел это. Я думаю, Gчто первая строка ничего не делает?


2
На самом деле что-то делает: ваш код производит дополнительную новую строку в конце вывода. ( Gдобавляет новую строку и содержимое пространства удержания к пространству шаблона. Хотя добавление содержимого пустого пространства удержания действительно безвредно,
новая строка

2

JavaScript (Node.js), 91 байт

console.log(require('fs').readFileSync(process.argv[2])+"".split(d="\n").reverse().join(d))

Вы имели в виду console.log((require('fs').readFileSync(process.argv[2])+"").split(d="\n").reverse().join(d))(92 байта)? Ваш текущий код не меняет линии.
Зубная щетка

2

Баш + коммунальные услуги, 25

tr \\n ^G|rev|tr ^G \\n|rev

Здесь ^Gбуквальный BELхарактер. Я предполагаю, что ввод только для печати ASCII.

Это trанонсирует весь ввод в одну строку, заменяя символы новой строки на BEL, затем revперебрасывает эту строку, затем trвозвращает обратно в многострочное, затем revснова перебирает каждую строку, чтобы получить желаемый результат.


2

МАТЛАБ, 44

@(x) strjoin(fliplr(strsplit(x,'\n')),'\n');

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


2

Юлия, 65 байт

open(s->print(join(reverse([l for l=readlines(s)]),"")),ARGS[1])

Это принимает файл в качестве аргумента командной строки и печатает его строки в обратном порядке. Конечные символы новой строки перемещаются на фронт, в отличие от того tac, что законно.

Ungolfed:

function p(s::Stream)
    # Create a vector of the lines of the input stream
    L = [l for l in readlines(s)]

    # Reverse the vector and join it back into a string
    j = join(reverse(L), "")

    # Print the string to STDOUT
    print(j)
end

# Open the file specified in the first command line argument
# and apply the function p to its contents
open(p, ARGS[1])

2

Пип , 3 + 2 = 5 байт

Использует rи nфлаги; читает со стандартного ввода.

RVg

rФлага считывает стандартный ввод и сохраняет его в виде списка линий в g(который , как правило , список командной строки ар г ы). Затем мы переворачиваем этот список, и он автоматически печатается. В nсписках флагов причины для вывода с новой строки в качестве разделителя.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.