TeX, 216 byte (masing-masing 4 baris, 54 karakter)
Karena ini bukan tentang jumlah byte, ini tentang kualitas output typeset :-)
{\let~\catcode~`A13 \defA#1{~`#113\gdef}AGG#1{~`#1 13%
\global\let}GFF\elseGHH\fiAQQ{Q}AII{\ifxQ}AEE#1#2#3|{%
I#3#2#1FE{#1#2}#3|H}ADD#1#2|{I#1FE{}#1#2|H}ACC#1#2|{D%
#2Q|#1 }ABBH#1 {HI#1FC#1|BH}\gdef\S#1{\iftrueBH#1 Q }}
Cobalah secara Online! (Overleaf; tidak yakin cara kerjanya)
File tes lengkap:
{\let~\catcode~`A13 \defA#1{~`#113\gdef}AGG#1{~`#1 13%
\global\let}GFF\elseGHH\fiAQQ{Q}AII{\ifxQ}AEE#1#2#3|{%
I#3#2#1FE{#1#2}#3|H}ADD#1#2|{I#1FE{}#1#2|H}ACC#1#2|{D%
#2Q|#1 }ABBH#1 {HI#1FC#1|BH}\gdef\S#1{\iftrueBH#1 Q }}
\S{swap the a first and last letters of each word}
pwas eht a tirsf dna tasl setterl fo hace dorw
\S{SWAP THE A FIRST AND LAST LETTERS OF EACH WORD}
\bye
Keluaran:

Untuk LaTeX Anda hanya perlu boilerplate:
\documentclass{article}
\begin{document}
{\let~\catcode~`A13 \defA#1{~`#113\gdef}AGG#1{~`#1 13%
\global\let}GFF\elseGHH\fiAQQ{Q}AII{\ifxQ}AEE#1#2#3|{%
I#3#2#1FE{#1#2}#3|H}ADD#1#2|{I#1FE{}#1#2|H}ACC#1#2|{D%
#2Q|#1 }ABBH#1 {HI#1FC#1|BH}\gdef\S#1{\iftrueBH#1 Q }}
\S{swap the a first and last letters of each word}
pwas eht a tirsf dna tasl setterl fo hace dorw
\S{SWAP THE A FIRST AND LAST LETTERS OF EACH WORD}
\end{document}
Penjelasan
TeX adalah binatang aneh. Membaca kode normal dan memahaminya adalah suatu prestasi tersendiri. Memahami kode TeX yang dikaburkan melangkah lebih jauh. Saya akan mencoba membuat hal ini dapat dimengerti oleh orang-orang yang juga tidak mengenal TeX, jadi sebelum kita mulai di sini adalah beberapa konsep tentang TeX untuk membuat hal-hal lebih mudah diikuti:
Untuk (tidak begitu) pemula TeX mutlak
Pertama, dan yang paling item yang penting dalam daftar ini: kode tidak tidak harus dalam bentuk persegi panjang, meskipun budaya pop mungkin menyebabkan Anda berpikir begitu .
TeX adalah bahasa ekspansi makro. Anda dapat, sebagai contoh, mendefinisikan \def\sayhello#1{Hello, #1!}dan kemudian menulis \sayhello{Code Golfists}untuk mendapatkan TeX untuk dicetak Hello, Code Golfists!. Ini disebut "undelimited macro", dan untuk memberikannya parameter pertama (dan hanya, dalam hal ini) Anda melampirkannya dalam kurung kurawal. TeX menghapus kawat gigi itu ketika makro mengambil argumen. Anda dapat menggunakan hingga 9 parameter: \def\say#1#2{#1, #2!}lalu \say{Good news}{everyone}.
Counterpart macro undelimited yang, tidak mengejutkan, yang dibatasi :) Anda bisa membuat definisi sebelumnya anak laki-laki lebih semantical : \def\say #1 to #2.{#1, #2!}. Dalam hal ini parameter diikuti oleh apa yang disebut teks parameter . Teks parameter tersebut membatasi argumen makro ( #1dibatasi oleh ␣to␣, spasi yang disertakan, dan #2dibatasi oleh .). Setelah definisi itu, Anda dapat menulis \say Good news to everyone., yang akan diperluas ke Good news, everyone!. Bagus bukan? :) Namun argumen yang dibatasi adalah (mengutip TeXbook ) “urutan token terpendek (mungkin kosong) dengan {...}grup bertumpuk dengan benar yang diikuti dalam input oleh daftar token non-parameter khusus ini”. Ini berarti perluasan dari\say Let's go to the mall to Martinakan menghasilkan kalimat yang aneh. Dalam hal ini Anda akan perlu untuk “menyembunyikan” yang pertama ␣to␣dengan {...}: \say {Let's go to the mall} to Martin.
Sejauh ini baik. Sekarang segalanya mulai menjadi aneh. Ketika TeX membaca sebuah karakter (yang didefinisikan oleh "kode karakter"), TeX akan memberikan karakter itu "kode kategori" (catcode, untuk teman-teman :) yang mendefinisikan apa arti karakter itu. Kombinasi kode karakter dan kategori ini membuat token (lebih lanjut tentang itu di sini , misalnya). Yang menarik bagi kami di sini pada dasarnya adalah:
catcode 11 , yang mendefinisikan token yang dapat membuat urutan kontrol (nama mewah untuk makro). Secara default semua huruf [a-zA-Z] adalah catcode 11, jadi saya bisa menulis \hello, yang merupakan satu urutan kontrol tunggal, sedangkan \he11ourutan kontrol \hediikuti oleh dua karakter 1, diikuti oleh huruf o, karena 1bukan catcode 11. Jika saya lakukan \catcode`1=11, sejak saat itu \he11oakan menjadi satu urutan kontrol. Satu hal penting adalah bahwa kode-kode ditetapkan ketika TeX pertama kali melihat karakter di tangan, dan kode seperti itu dibekukan ... SELAMANYA! (syarat dan ketentuan berlaku)
catcode 12 , yang merupakan sebagian besar karakter lain, seperti 0"!@*(?,.-+/dan sebagainya. Mereka adalah jenis catcode yang paling tidak istimewa karena hanya berfungsi untuk menulis hal-hal di atas kertas. Tapi, hei, siapa yang menggunakan TeX untuk menulis?!? (sekali lagi, syarat dan ketentuan berlaku)
catcode 13 , yang mana sih :) Sungguh. Berhentilah membaca dan lakukan sesuatu dari hidup Anda. Anda tidak ingin tahu apa itu catcode 13. Pernah dengar hari Jumat, tanggal 13? Tebak dari mana namanya berasal! Lanjutkan dengan risiko Anda sendiri! Karakter catcode 13, juga disebut karakter "aktif", bukan hanya karakter lagi, itu adalah makro itu sendiri! Anda dapat mendefinisikannya untuk memiliki parameter dan memperluas ke sesuatu seperti yang kita lihat di atas. Setelah Anda melakukan \catcode`e=13Anda berpikir Anda bisa melakukannya \def e{I am the letter e!}, TAPI. KAMU. TIDAK BISA! ebukan surat lagi, jadi \defbukankah \defAnda tahu, itu \d e f! Oh, pilih surat lain yang Anda katakan? Baik! \catcode`R=13 \def R{I am an ARRR!}. Baiklah, Jimmy, coba! Saya berani Anda melakukan itu dan menulis Rkode Anda! Itulah yang dimaksud dengan catcode 13. SAYA TENANG! Mari kita lanjutkan.
Oke, sekarang untuk pengelompokan. Ini cukup mudah. Tugas apa pun ( \defmerupakan operasi penugasan, \let(kami akan membahasnya) adalah tugas lain) yang dilakukan dalam suatu grup dikembalikan ke posisi semula sebelum grup itu dimulai kecuali tugas itu bersifat global. Ada beberapa cara untuk memulai grup, salah satunya adalah dengan karakter catcode 1 dan 2 (oh, kode cat lagi). Secara default {adalah catcode 1, atau begin-group, dan }catcode 2, atau end-group. Contoh: \def\a{1} \a{\def\a{2} \a} \aIni mencetak 1 2 1. Di luar grup \aada 1, lalu di dalamnya didefinisikan ulang 2, dan ketika grup berakhir, ia dikembalikan ke 1.
The \letoperasi tugas operasi lain seperti \def, tapi agak berbeda. Dengan \defAnda menentukan makro yang akan diperluas ke hal-hal, dengan \letAnda membuat salinan dari hal-hal yang sudah ada. Setelah \let\blub=\def( =opsional) Anda dapat mengubah awal econtoh dari item catcode 13 di atas \blub e{...dan bersenang-senang dengan yang itu. Atau lebih baik, bukannya melanggar hal-hal yang Anda dapat memperbaiki (yang akan Anda lihat itu!) Yang Rmisalnya: \let\newr=R \catcode`R=13 \def R{I am an A\newr\newr\newr!}. Pertanyaan cepat: dapatkah Anda mengganti nama \newR?
Akhirnya, yang disebut "ruang palsu". Ini semacam topik yang tabu karena ada orang yang mengklaim bahwa reputasi yang diperoleh di TeX - LaTeX Stack Exchange dengan menjawab pertanyaan "ruang palsu" tidak boleh dipertimbangkan, sementara yang lain dengan sepenuh hati tidak setuju. Dengan siapa Anda setuju? Tempatkan taruhan Anda! Sementara itu: TeX memahami jeda baris sebagai spasi. Cobalah untuk menulis beberapa kata dengan jeda baris (bukan baris kosong ) di antaranya. Sekarang tambahkan %di akhir baris ini. Sepertinya Anda “berkomentar” pada ruang-ruang ujung ini. Itu dia :)
(Semacam) tidak mengubah kode
Mari kita buat persegi panjang itu menjadi sesuatu (bisa dibilang) lebih mudah diikuti:
{
\let~\catcode
~`A13
\defA#1{~`#113\gdef}
AGG#1{~`#113\global\let}
GFF\else
GHH\fi
AQQ{Q}
AII{\ifxQ}
AEE#1#2#3|{I#3#2#1FE{#1#2}#3|H}
ADD#1#2#3|{I#2FE{#1}#2#3|H}
ACC#1#2|{D{}#2Q|#1 }
ABBH#1 {HI#1FC#1|BH}
\gdef\S#1{\iftrueBH#1 Q }
}
Penjelasan dari setiap langkah
setiap baris berisi satu instruksi tunggal. Mari kita pergi satu per satu, membedah mereka:
{
Pertama-tama kita memulai sebuah kelompok untuk menjaga beberapa perubahan (yaitu perubahan catcode) lokal sehingga mereka tidak mengacaukan teks input.
\let~\catcode
Pada dasarnya semua kode kebingungan TeX dimulai dengan instruksi ini. Secara default, baik dalam TeX polos dan LaTeX, ~karakter adalah karakter aktif yang dapat dibuat menjadi makro untuk digunakan lebih lanjut. Dan alat terbaik untuk mengubah kode TeX adalah perubahan catcode, jadi ini umumnya pilihan terbaik. Sekarang alih-alih \catcode`A=13kita dapat menulis ~`A13( =ini opsional):
~`A13
Sekarang surat itu Aadalah karakter aktif, dan kita dapat mendefinisikannya untuk melakukan sesuatu:
\defA#1{~`#113\gdef}
Asekarang merupakan makro yang mengambil satu argumen (yang seharusnya merupakan karakter lain). Pertama-tama catcode argumen diubah menjadi 13 untuk membuatnya aktif: ~`#113(ganti ~dengan \catcodedan tambahkan =dan Anda punya :) \catcode`#1=13. Akhirnya ia meninggalkan \gdef(global \def) dalam aliran input. Singkatnya, Abuat karakter lain aktif dan mulai definisinya. Mari kita coba:
AGG#1{~`#113\global\let}
AG"aktifkan" pertama Gdan lakukan \gdef, yang diikuti oleh yang berikutnya Gmemulai definisi. Definisi Gini sangat mirip dengan A, kecuali bahwa alih-alih \gdefitu melakukan \global\let(tidak ada \gletseperti \gdef). Singkatnya, Gmengaktifkan karakter dan membuatnya menjadi sesuatu yang lain. Mari kita buat pintasan untuk dua perintah yang akan kita gunakan nanti:
GFF\else
GHH\fi
Sekarang alih-alih \elsedan \fikita cukup menggunakan Fdan H. Jauh lebih pendek :)
AQQ{Q}
Sekarang kita gunakan Alagi untuk mendefinisikan makro lain Q,. Pernyataan di atas pada dasarnya tidak (dalam bahasa yang kurang dikaburkan) \def\Q{\Q}. Ini bukan definisi yang sangat menarik, tetapi memiliki fitur yang menarik. Kecuali Anda ingin memecah beberapa kode, satu-satunya makro yang berkembang Qadalah Qdirinya sendiri, sehingga berfungsi seperti penanda unik (disebut quark ). Anda bisa menggunakan \ifxpersyaratan untuk menguji apakah argumen makro adalah quark dengan \ifx Q#1:
AII{\ifxQ}
sehingga Anda dapat yakin bahwa Anda menemukan spidol tersebut. Perhatikan bahwa dalam definisi ini saya menghapus ruang antara \ifxdan Q. Biasanya ini akan menyebabkan kesalahan (perhatikan bahwa highlight sintaksis berpikir itu \ifxQadalah satu hal), tetapi karena sekarang Qadalah katak 13 maka tidak dapat membentuk urutan kontrol. Namun, berhati-hatilah untuk tidak memperluas kuark ini atau Anda akan terjebak dalam loop tak terbatas karena Qmemperluas Qyang memperluas ke Qmana ...
Sekarang setelah pendahuluan telah selesai, kita dapat pergi ke algoritma yang tepat untuk mengatur setterl. Karena tokenization TeX, algoritma harus ditulis mundur. Ini karena pada saat Anda melakukan definisi, TeX akan melakukan tokenize (menetapkan kode) ke karakter dalam definisi menggunakan pengaturan saat ini jadi, misalnya, jika saya lakukan:
\def\one{E}
\catcode`E=13\def E{1}
\one E
hasilnya adalah E1, sedangkan jika saya mengubah urutan definisi:
\catcode`E=13\def E{1}
\def\one{E}
\one E
outputnya adalah 11. Ini karena pada contoh pertama Edefinisi dalam tokenized sebagai huruf (catcode 11) sebelum perubahan catcode, jadi itu akan selalu menjadi huruf E. Namun, pada contoh kedua, Epertama kali diaktifkan, dan baru kemudian \oneditetapkan, dan sekarang definisi tersebut berisi katak 13 Eyang diperluas 1.
Namun, saya akan mengabaikan fakta ini dan menyusun ulang definisi untuk memiliki urutan logis (tetapi tidak berfungsi). Dalam paragraf berikut Anda dapat mengasumsikan bahwa surat-surat B, C, D, dan Eaktif.
\gdef\S#1{\iftrueBH#1 Q }
(perhatikan ada bug kecil di versi sebelumnya, itu tidak mengandung ruang terakhir dalam definisi di atas. Saya hanya memperhatikannya saat menulis ini. Baca terus dan Anda akan melihat mengapa kita membutuhkannya untuk menghentikan makro dengan benar. )
Pertama kita mendefinisikan makro tingkat pengguna \S,. Yang ini seharusnya tidak menjadi karakter aktif untuk memiliki sintaks ramah (?), Jadi makro untuk gwappins dan setterl adalah \S. Makro dimulai dengan kondisi selalu benar \iftrue(akan segera menjadi jelas mengapa), dan kemudian memanggil Bmakro diikuti oleh H(yang telah kita tentukan sebelumnya \fi) untuk mencocokkan \iftrue. Lalu kita meninggalkan argumen makro #1diikuti oleh spasi dan oleh quark Q. Misalkan kita gunakan \S{hello world}, maka input streamakan terlihat seperti ini: \iftrue BHhello world Q␣(Saya mengganti ruang terakhir dengan ␣sehingga rendering situs tidak memakannya, seperti yang saya lakukan pada versi kode sebelumnya). \iftrueitu benar, jadi itu mengembang dan kita dibiarkan BHhello world Q␣. TeX tidak menghapus \fi( H) setelah kondisi dievaluasi, melainkan membiarkannya di sana sampai benar\fi - benar diperluas. Sekarang Bmakro diperluas:
ABBH#1 {HI#1FC#1|BH}
Badalah makro terbatas yang memiliki parameter teks H#1␣, jadi argumennya adalah apa pun yang ada di antara Hdan spasi. Melanjutkan contoh di atas aliran input sebelum perluasan Bis BHhello world Q␣. Bdiikuti oleh H, sebagaimana mestinya (jika TeX akan menimbulkan kesalahan), maka ruang berikutnya adalah antara hellodan world, begitu #1juga kata hello. Dan di sini kita harus membagi teks input di spasi. Yay: D Perluasan Bmenghapus segala sesuatu sampai ke ruang pertama dari input stream dan menggantikan dengan HI#1FC#1|BHdengan #1menjadi hello: HIhelloFChello|BHworld Q␣. Perhatikan bahwa ada yang baru BHnanti dalam aliran input, untuk melakukan rekursi ekorBdan memproses kata-kata selanjutnya. Setelah kata ini diproses, Bproses kata berikutnya hingga kata yang akan diproses adalah kuark Q. Ruang terakhir setelah Qdiperlukan karena makro dibatasi B membutuhkan satu di akhir argumen. Dengan versi sebelumnya (lihat sunting histori) kode akan bertingkah jika Anda menggunakan \S{hello world}abc abc(ruang antara abcs akan lenyap).
OK, kembali ke input stream: HIhelloFChello|BHworld Q␣. Pertama ada H( \fi) yang melengkapi inisial \iftrue. Sekarang kita punya ini (pseudocoded):
I
hello
F
Chello|B
H
world Q␣
The I...F...Hberpikir sebenarnya adalah \ifx Q...\else...\fistruktur. The \ifxpemeriksaan tes jika (pertama tanda yang) kata meraih adalah Qquark. Jika tidak ada yang lain untuk melakukan dan berakhir eksekusi, jika apa yang tersisa adalah: Chello|BHworld Q␣. Sekarang Cdiperluas:
ACC#1#2|{D#2Q|#1 }
Argumen pertama Cadalah undelimited, jadi kecuali bersiap akan menjadi tanda tunggal, Argumen kedua dibatasi oleh |, jadi setelah ekspansi C(dengan #1=hdan #2=ello) input stream adalah: DelloQ|h BHworld Q␣. Perhatikan bahwa lain |yang diletakkan di sana, dan hdari hellodiletakkan setelah itu. Setengah swapping dilakukan; huruf pertama ada di akhir. Di TeX, mudah untuk mengambil token pertama dari daftar token. Makro sederhana \def\first#1#2|{#1}mendapatkan huruf pertama saat Anda menggunakan \first hello|. Yang terakhir adalah masalah karena TeX selalu mengambil daftar token "terkecil, mungkin kosong" sebagai argumen, jadi kita perlu beberapa solusi. Item berikutnya dalam daftar token adalah D:
ADD#1#2|{I#1FE{}#1#2|H}
DMakro
ini adalah salah satu solusi dan berguna dalam kasus tunggal di mana kata memiliki satu huruf. Andaikan hellokita tidak melakukannya x. Dalam hal ini input stream akan DQ|x, maka Dakan memperluas (dengan #1=Q, dan #2mengosongkan) ke: IQFE{}Q|Hx. Ini mirip dengan blok I...F...H( \ifx Q...\else...\fi) di B, yang akan melihat bahwa argumen adalah quark dan akan mengganggu eksekusi hanya menyisakan xuntuk pengaturan huruf. Dalam kasus lain (kembali ke hellocontoh), Dakan memperluas (dengan #1=edan #2=lloQ) ke: IeFE{}elloQ|Hh BHworld Q␣. Sekali lagi, I...F...Hakan memeriksa Qtapi akan gagal dan mengambil \elsecabang: E{}elloQ|Hh BHworld Q␣. Sekarang bagian terakhir dari hal ini, theE makro akan berkembang:
AEE#1#2#3|{I#3#2#1FE{#1#2}#3|H}
Teks parameter di sini sangat mirip dengan Cdan D; argumen pertama dan kedua tidak didahulukan, dan yang terakhir dibatasi oleh |. Input stream terlihat seperti ini: E{}elloQ|Hh BHworld Q␣, kemudian Emengembang (dengan #1kosong, #2=edan #3=lloQ): IlloQeFE{e}lloQ|HHh BHworld Q␣. Lain I...F...Hblok cek untuk quark (yang melihat ldan kembali false): E{e}lloQ|HHh BHworld Q␣. Sekarang Ememperluas lagi (dengan #1=ekosong, #2=ldan #3=loQ): IloQleFE{el}loQ|HHHh BHworld Q␣. Dan lagi I...F...H. Makro melakukan beberapa iterasi lagi sampai Qakhirnya ditemukan dan truecabang diambil: E{el}loQ|HHHh BHworld Q␣-> IoQlelFE{ell}oQ|HHHHh BHworld Q␣-> E{ell}oQ|HHHHh BHworld Q␣-> IQoellFE{ello}Q|HHHHHh BHworld Q␣. Sekarang quark ditemukan dan mengembang bersyarat untuk: oellHHHHh BHworld Q␣. Fiuh.
Oh, tunggu, apa ini? SURAT NORMAL? Oh Boy! Surat-surat yang akhirnya menemukan dan TeX menuliskan oell, kemudian sekelompok H( \fi) ditemukan dan diperluas (apa-apa) meninggalkan input stream dengan: oellh BHworld Q␣. Sekarang kata pertama memiliki huruf pertama dan terakhir ditukar dan apa TeX menemukan selanjutnya adalah yang lain Buntuk mengulangi seluruh proses untuk kata berikutnya.
}
Akhirnya kami mengakhiri grup mulai kembali ke sana sehingga semua tugas lokal dibatalkan. Tugas lokal adalah perubahan catcode huruf A, B, C, ... yang dibuat macro sehingga mereka kembali ke surat arti normal mereka dan dapat digunakan dengan aman dalam teks. Dan itu saja. Sekarang \Smakro yang didefinisikan kembali di sana akan memicu pemrosesan teks seperti di atas.
Satu hal yang menarik tentang kode ini adalah bahwa kode ini sepenuhnya dapat dikembangkan. Artinya, Anda bisa menggunakannya dengan aman dalam memindahkan argumen tanpa khawatir itu akan meledak. Anda bahkan dapat menggunakan kode untuk memeriksa apakah huruf terakhir dari sebuah kata sama dengan yang kedua (untuk alasan apa pun Anda membutuhkannya) dalam \ifujian:
\if\S{here} true\else false\fi % prints true (plus junk, which you would need to handle)
\if\S{test} true\else false\fi % prints false
Maaf untuk penjelasan bertele-tele (mungkin terlalu). Saya mencoba membuatnya sejelas mungkin untuk non TeXies juga :)
Ringkasan untuk yang tidak sabar
Makro \Smendahului input dengan karakter aktif Byang mengambil daftar token yang dibatasi oleh ruang final dan meneruskannya ke C. Cmengambil token pertama dalam daftar itu dan memindahkannya ke akhir daftar token dan memperluas Ddengan apa yang tersisa. Dmemeriksa apakah "apa yang tersisa" kosong, dalam hal ditemukan satu kata, tidak melakukan apa-apa; jika tidak mengembang E. Eloop melalui daftar token sampai menemukan huruf terakhir dalam kata, ketika ditemukan meninggalkan huruf terakhir, diikuti oleh tengah kata, yang kemudian diikuti oleh huruf pertama yang tersisa di ujung token stream oleh C.
Hello, world!menjadi,elloH !orldw(menukar tanda baca sebagai huruf) atauoellH, dorlw!(mempertahankan tanda baca di tempat)?