Реализация «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.

orlp
источник
8

FlogScript , 2 байта

)"

(Попробуйте на анархии гольф .)

Режим )enable --in-out-line-array, а остальная часть программы - "реверсирование массива строк.

Линн
источник
Ааа, ты меня обыграл!
mbomb007
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.

jkabrg
источник
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`` объединит массив в строку

Downgoat
источник
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.

LegionMammal978
источник
3

sed, 9 байт

1!G;h;$!d

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

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

Perl, 16 байт

print reverse<>
Стив
источник
@ Денис, упс, теперь вернулся.
Стив
3

Powershell, 41 байт

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

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

sweerpotato
источник
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.
manatwork
источник
Я думаю, что вы можете сделать 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
3

СНОБОЛ, 42 байта

S S =INPUT CHAR(10) S :S(S)
 OUTPUT =S
END
ninjalj
источник
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
manatwork
источник
2

Калий , 90 байт 86 байт

use IO;func main(){c=File.readLines(args[0]);for(x=c.length-1;x>=0; println(c[x--]))0;

Смотрите расширенный здесь

Яков Мисирян
источник
1
Я могу сократить это, злоупотребляя forсинтаксисом. Смотрите образец здесь
FryAmTheEggman
Хороший звонок @FryAmTheEggman! Я добавил это.
Джейкоб Мисирян
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списках флагов причины для вывода с новой строки в качестве разделителя.

DLosc
источник