text:
pwaS eht tirsf dna tasl setterl fo hace dorw
Or, "Swap the first and last letters of each word"
Your challenge is to, given a string of alphabetical ASCII characters as well as one other character to use as a delimiter (to separate each word), swap the first and last letters of each word. If there is a one-character word, leave it alone.
The examples/testcases use the lowercase letters and the space as the delimiter.
You do not need to handle punctuation; all of the inputs will only consist of the letters a through z, separated by a delimiter, all of a uniform case.
For example, with the string "hello world":
Input string: "hello world"
Identify each word: "[hello] [world]"
Identify the first and last letters of each word: "[[h]ell[o]] [[w]orl[d]]"
Swap the first letters of each word: "[[o]ell[h]] [[d]orl[w]]"
Final string: "oellh dorlw"

NOTE: the delimiter does not need to be inputted separately. The delimiter is just the character used to separate words. It can be anything. I wanted to leave options open for creative golfers, so I did not want to limit it to just spaces or new lines. The delimiter is just a character that separates words in the input string.
Test cases:
"swap the first and last letters of each word" -> "pwas eht tirsf dna tasl setterl fo hace dorw"
"hello world" -> "oellh dorlw"
"test cases" -> "test sasec"
"programming puzzles and code golf" -> "grogramminp suzzlep dna eodc folg"
"in a green meadow" -> "ni a nreeg weadom"
"yay racecar" -> "yay racecar"

How should punctuation be treated? Hello, world! becomes ,elloH !orldw (swapping punctuation as a letter) or oellH, dorlw! (keeping punctuation in place)?

@PhelypeOleinik You do not need to handle punctuation; all of the inputs will only consist of the letters a through z, and all a uniform case.

Second paragraph reads as well as one other character to use as a delimiter while the fourth reads separated by spaces. Which one is it?

@Adám Any non-alphabetic character. I’ll edit to clarify.

Can we return a list/array of words?

@BenjaminUrquhart Preferably no.

@ComradeSparklePony alright. Is returning a string ok?

@BenjaminUrquhart Yes. You can take input as a function argument if you want as well.

can NUL chars be a separator as well?

@u_ndefined Sure.

Are trailing spaces allowed?  (e.g. "Here is input" > "ereH si tnpui ")

@Chronocidal Sure.

@Arnauld Edited to clarify. Is that better?

@ComradeSparklePony Yes it is. Thanks!

Does the input and output delimiter need to be the same, or are we allowed to let's say take the input newline delimited and output space delimited?

@KevinCruijssen I would prefer both input and output to use the same delimiter. Hopefully this isn't too much trouble.

TeX, 216 bytes (4 lines, 54 characters each)
Because it's not about the byte count, it's about the quality of the typeset output :-)
{\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 }}

Try it Online! (Overleaf; not sure how it works)
Full test file:
{\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

Output:

For LaTeX you just need the 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}

Explanation
TeX is a strange beast. Reading normal code and understanding it is a feat by itself. Understanding obfuscated TeX code goes a few steps further. I'll try to make this understandable for people who don't know TeX as well, so before we start here's a few concepts about TeX to make things easier to follow:
For (not so) absolute TeX beginners

First, and most important item in this list: the code does not have to be in rectangle shape, even though pop culture might lead you to think so.
TeX is a macro expansion language. You can, as an example, define \def\sayhello#1{Hello, #1!} and then write \sayhello{Code Golfists} to get TeX to print Hello, Code Golfists!. This is called an “undelimited macro”, and to feed it the first (and only, in this case) parameter you enclose it in braces. TeX removes those braces when the macro grabs the argument. You can use up to 9 parameters: \def\say#1#2{#1, #2!} then \say{Good news}{everyone}.
The counterpart of undelimited macros are, unsurprisingly, delimited ones :) You could make the previous definition a tad more semantical: \def\say #1 to #2.{#1, #2!}. In this case the parameters are followed by so-called parameter text. Such parameter text delimits the argument of the macro (#1 is delimited by ␣to␣, spaces included, and #2 is delimited by .). After that definition you can write \say Good news to everyone., which will expand to Good news, everyone!. Nice, isn't it? :) However a delimited argument is (quoting the TeXbook) “the shortest (possibly empty) sequence of tokens with properly nested {...} groups that is followed in the input by this particular list of non-parameter tokens”. This means that the expansion of \say Let's go to the mall to Martin will produce a weird sentence. In this case you'd need to “hide” the first ␣to␣ with {...}: \say {Let's go to the mall} to Martin.
So far so good. Now things start to get weird. When TeX reads a character (which is defined by a “character code”), it assigns that character a “category code” (catcode, for friends :) which defines what that character will mean. This combination of character and category code makes a token (more on that here, for example). The ones which are of interest for us here are basically:

catcode 11, which define tokens which can make up a control sequence (a posh name for a macro). By default all letters [a-zA-Z] are catcode 11, so I can write \hello, which is one single control sequence, while \he11o is the control sequence \he followed by two characters 1, followed by the letter o, because 1 is not catcode 11. If I did \catcode`1=11, from that point on \he11o would be one control sequence. One important thing is that catcodes are set when TeX first sees the character at hand, and such catcode is frozen... FOREVER! (terms and conditions may apply)
catcode 12, which are most of other characters, such as 0"!@*(?,.-+/ and so forth. They are the least special type of catcode as they serve only for writing stuff on the paper. But hey, who uses TeX for writing?!? (again, terms and conditions may apply)
catcode 13, which is hell :) Really. Stop reading and go do something out of your life. You don't want to know what catcode 13 is. Ever heard of Friday, 13th? Guess where it got its name from! Continue at your own risk! A catcode 13 character, also called an “active” character, is not just a character anymore, it is a macro itself! You can define it to have parameters and expand to something like we saw above. After you do \catcode`e=13 you think you can do \def e{I am the letter e!}, BUT. YOU. CANNOT! e is not a letter anymore, so \def is not the \def you know, it is \d e f! Oh, choose another letter you say? Okay! \catcode`R=13 \def R{I am an ARRR!}. Very well, Jimmy, try it! I dare you do that and write an R in your code! That's what a catcode 13 is. I AM CALM! Let's move on.

Okay, now to grouping. This is fairly straightforward. Whatever assignments (\def is an assignment operation, \let (we'll get into it) is another) done in a group are restored to what they were before that group started unless that assignment is global. There are several ways to start groups, one of them is with catcode 1 and 2 characters (oh, catcodes again). By default { is catcode 1, or begin-group, and } is catcode 2, or end-group. An example: \def\a{1} \a{\def\a{2} \a} \a This prints 1 2 1. Outside the group \a was 1, then inside it was redefined to 2, and when the group ended, it was restored to 1.
The \let operation is another assignment operation like \def, but rather different. With \def you define macros which will expand to stuff, with \let you create copies of already existing things. After \let\blub=\def (the = is optional) you can change the start of the e example from the catcode 13 item above to \blub e{... and have fun with that one. Or better, instead of breaking stuff you can fix (would you look at that!) the R example: \let\newr=R \catcode`R=13 \def R{I am an A\newr\newr\newr!}. Quick question: could you rename to \newR?
Finally, the so-called “spurious spaces”. This is kind of a taboo topic because there are people who claim that reputation earned in the TeX - LaTeX Stack Exchange by answering “spurious spaces” questions should not be considered, while others wholeheartedly disagree. Whom do you agree with? Place your bets! Meanwhile: TeX understands a line break as a space. Try to write several words with a line break (not an empty line) between them. Now add a % at the end of these lines. It's like you were “commenting out” these end-of-line spaces. That's it :)

(Sort of) ungolfing the code
Let's make that rectangle into something (arguably) easier to follow:
{
\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 }
}

Explanation of each step
each line contains one single instruction. Let's go one by one, dissecting them:
{
First we start a group to keep some changes (namely catcode changes) local so that they don't mess up the input text.
\let~\catcode
Basically all TeX obfuscation codes start with this instruction. By default, both in plain TeX and LaTeX, the ~ character is the one active character which can be made into a macro for further use. And the best tool for weirdifying TeX code are catcode changes, so this is generally the best choice. Now instead of \catcode`A=13 we can write ~`A13 (the = is optional):
~`A13
Now the letter A is an active character, and we can define it to do something:
\defA#1{~`#113\gdef}
A is now a macro that takes one argument (which should be another character). First the catcode of the argument is changed to 13 to make it active: ~`#113 (replace the ~ by \catcode and add an = and you have: \catcode`#1=13). Finally it leaves a \gdef (global \def) in the input stream. In short, A makes another character active and start its definition. Let's try it:
AGG#1{~`#113\global\let}
AG first “activates” G and does \gdef, which followed by the next G starts the definition. The definition of G is very similar to that of A, except that instead of \gdef it does a \global\let (there isn't a \glet like the \gdef). In short, G activates a character and makes it be something else. Let's make shortcuts for two commands we'll use later:
GFF\else
GHH\fi
Now instead of \else and \fi we can simply use F and H. Much shorter :)
AQQ{Q}
Now we use A again to define another macro, Q. The above statement basically does (in a less obfuscated language) \def\Q{\Q}. This isn't a terribly interesting definition, but it has an interesting feature. Unless you do want to break some code, the only macro that expands to Q is Q itself, so it acts like a unique marker (it's called a quark). You can use the \ifx conditional to test if the argument of a macro is such quark with \ifx Q#1:
AII{\ifxQ}
so you can be pretty sure that you found such a marker. Notice that in this definition I removed the space between \ifx and Q. Usually this would lead to an error (note that the syntax highlight thinks that \ifxQ is one thing), but since now Q is catcode 13 it cannot form a control sequence. Be careful, however, not to expand this quark or you'll get stuck in an infinite loop because Q expands to Q which expands to Q which...
Now that the preliminaries are done, we can go to the proper algorithm to pwas eht setterl. Due to TeX's tokenization the algorithm has to be written backwards. This is because at the time you do a definition TeX will tokenize (assign catcodes) to the characters in the definition using the current settings so, for example, if I do:
\def\one{E}
\catcode`E=13\def E{1}
\one E

the output is E1, whereas if I change the order of the definitions:
\catcode`E=13\def E{1}
\def\one{E}
\one E

the output is 11. This is because in the first example the E in the definition was tokenized as a letter (catcode 11) before the catcode change, so it will always be a letter E. In the second example, however, E was first made active, and only then \one was defined, and now the definition contains the catcode 13 E which expands to 1.
I will, however, overlook this fact and reorder the definitions to have a logical (but not working) order. In the following paragraphs you can assume that the letters B, C, D, and E are active.
\gdef\S#1{\iftrueBH#1 Q }
(notice there was a small bug in the previous version, it did not contain the final space in the definition above. I only noticed it while writing this. Read on and you'll see why we need that one to properly terminate the macro.)
First we define the user-level macro, \S. This one shouldn't be an active character to have a friendly (?) syntax, so the macro for gwappins eht setterl is \S. The macro starts with an always-true conditional \iftrue (it will soon be clear why), and then calls the B macro followed by H (which we defined earlier to be \fi) to match the \iftrue. Then we leave the argument of the macro #1 followed by a space and by the quark Q. Suppose we use \S{hello world}, then the input stream should look like this: \iftrue BHhello world Q␣ (I replaced the last space by a ␣ so that the rendering of the site does not eat it, like I did in the previous version of the code). \iftrue is true, so it expands and we are left with BHhello world Q␣. TeX does not remove the \fi (H) after the conditional is evaluated, instead it leaves it there until the \fi is actually expanded. Now the B macro is expanded:
ABBH#1 {HI#1FC#1|BH}
B is a delimited macro whose parameter text is H#1␣, so the argument is whatever is between H and a space. Continuing the example above the input stream prior to the expansion of B is BHhello world Q␣. B is followed by H, as it should (otherwise TeX would raise an error), then the next space is between hello and world, so #1 is the word hello. And here we got to split the input text at the spaces. Yay :D The expansion of B removes everything up to the first space from the input stream and replaces by HI#1FC#1|BH with #1 being hello: HIhelloFChello|BHworld Q␣. Notice that there is a new BH later in the input stream, to do a tail recursion of B and process later words. After this word is processed B processes the next word until the word-to-be-processed is the quark Q. The last space after Q is needed because the delimited macro B requires one at the end of the argument. With the previous version (see edit history) the code would misbehave if you used \S{hello world}abc abc (the space between the abcs would vanish).
OK, back to the input stream: HIhelloFChello|BHworld Q␣. First there's the H (\fi) that completes the initial \iftrue. Now we have this (pseudocoded):
I
  hello
F
  Chello|B
H
world Q␣

The I...F...H think is actually a \ifx Q...\else...\fi structure. The \ifx test checks if the (first token of the) word grabbed is the Q quark. If it is there is nothing else to do and the execution terminates, otherwise what remains is: Chello|BHworld Q␣. Now C is expanded:
ACC#1#2|{D#2Q|#1 }
The first argument of C is undelimited, so unless braced it will be a single token, The second argument is delimited by |, so after the expansion of C (with #1=h and #2=ello) the input stream is: DelloQ|h BHworld Q␣. Notice that another | is put there, and the h of hello is put after that. Half the swapping is done; the first letter is at the end. In TeX it is easy to grab the first token of a token list. A simple macro \def\first#1#2|{#1} gets the first letter when you use \first hello|. The last one is a problem because TeX always grabs the “smallest, possibly empty” token list as argument, so we need a few work-arounds. Next item in the token list is D:
ADD#1#2|{I#1FE{}#1#2|H}
This D macro is one of the work-arounds and it's useful in the sole case where the word has a single letter. Suppose instead of hello we had x. In this case the input stream would be DQ|x, then D would expand (with #1=Q, and #2 empty) to: IQFE{}Q|Hx. This is similar to the I...F...H (\ifx Q...\else...\fi) block in B, which will see that the argument is the quark and will interrupt the execution leaving only x for typesetting. In other cases (returning to the hello example), D would expand (with #1=e and #2=lloQ) to: IeFE{}elloQ|Hh BHworld Q␣. Again, the I...F...H will check for Q but will fail and take the \else branch: E{}elloQ|Hh BHworld Q␣. Now the last piece of this thing, the E macro would expand:
AEE#1#2#3|{I#3#2#1FE{#1#2}#3|H}
The parameter text here is quite similar to C and D; the first and second arguments are undelimited, and the last one is delimited by |. The input stream looks like this: E{}elloQ|Hh BHworld Q␣, then E expands (with #1 empty, #2=e, and #3=lloQ): IlloQeFE{e}lloQ|HHh BHworld Q␣. Another I...F...H block checks for the quark (which sees l and returns false): E{e}lloQ|HHh BHworld Q␣. Now E expands again (with #1=e empty, #2=l, and #3=loQ): IloQleFE{el}loQ|HHHh BHworld Q␣. And again I...F...H. The macro does a few more iterations until the Q is finally found and the true branch is taken: E{el}loQ|HHHh BHworld Q␣ -> IoQlelFE{ell}oQ|HHHHh BHworld Q␣ -> E{ell}oQ|HHHHh BHworld Q␣-> IQoellFE{ello}Q|HHHHHh BHworld Q␣. Now the quark is found and the conditional expands to: oellHHHHh BHworld Q␣. Phew.
Oh, wait, what are these? NORMAL LETTERS? Oh, boy! The letters are finally found and TeX writes down oell, then a bunch of H (\fi) are found and expanded (to nothing) leaving the input stream with: oellh BHworld Q␣. Now the first word has the first and last letters swapped and what TeX finds next is the other B to repeat the whole process for the next word.
}
Finally we end the group started back there so that all local assignments are undone. The local assignments are the catcode changes of the letters A, B, C, ... which were made macros so that they return to their normal letter meaning and can be safely used in the text. And that's it. Now the \S macro defined back there will trigger the processing of the text as above.
One interesting thing about this code is that it is fully expandable. That is, you can safely use it in moving arguments without worrying that it will explode. You can even use the code to check if the last letter of a word is the same as the second (for whatever reason you would need that) in an \if test:
\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

Sorry for the (probably far too) wordy explanation. I tried to make it as clear as possible for non TeXies as well :)
Summary for the impatient
The macro \S prepends the input with an active character B which grabs lists of tokens delimited by a final space and passes them to C. C takes the first token in that list and moves it to the end of the token list and expands D with what remains. D checks if “what remains” is empty, in which case a single-letter word was found, then do nothing; otherwise expands E. E loops through the token list until it finds the last letter in the word, when it is found it leaves that last letter, followed by the middle of the word, which is then followed by the first letter left at the end of the token stream by C.

I would love a full explanation of this one. I'm very curious as to how it works!

@LambdaBeta I can do it, but not right now. Hold on and I'll ping you when I do it :)

@LambdaBeta Done! Sorry, I'm too wordy sometimes :-)

JavaScript (ES6),  39  36 bytes
Saved 3 bytes thanks to @FryAmTheEggman
Uses a linefeed as separator.
s=>s.replace(/(.)(.*)(.)/g,'$3$2$1')

Try it online!

(.)(.*)(.) is that the Total Recall emoticon?

@MikeTheLiar Kind of, I guess. :D

The assignment specifies the string contains the separator.

@CeesTimmerman I'm not sure what you mean. This code expects a linefeed as separator and therefore takes strings with linefeeds as input. (The footer of the TIO link converts spaces to linefeeds and then back to spaces for readability.)

"given a string of alphabetical ASCII characters as well as one other character to use as a delimiter (to separate each word)" - Nm, i thought it was a separate parameter.

Retina, 8 5 bytes
,V,,`

Try it online!
Saved 3 bytes thanks to Kevin Cruijssen!
Uses a newline as the separator. We make use of Retina's reverse stage and some limits. The first limit is which matches to apply the reversal to, so we pick all of them with ,. Then we want the first and last letter of each match to be swapped, so we take each letter in the range ,, which translates to a range from the beginning to the end with step size zero.

Dangit, I was just searching through the docs for something like this to update my answer, but you beat me to it. I knew about V, but didn't knew it could be used with the indices 1,-2 like that. Nice one!

@KevinCruijssen I cheated a little and reviewed how limit ranges worked while this was in the sandbox :) I still feel like there should be a better way than inverting a range but I haven't been able to find anything shorter.

You're indeed right that it can be shorter without a limit-range, because it seems this 5-byter works (given as example at the bottom of the Step Limits in the docs).

@KevinCruijssen Nice! Can't believe I missed that.

So, 5 bytes and only 3 different characters? That's minimalist.

Pepe, 107 105 bytes
REEeREeeEeeeeerEEreREEEeREEEEEEeREEEErEEREEEEEEEreererEEEeererEEEerEEeERrEEEeerEEeerereeerEEEEeEEEReEeree

Try it online!
Explanation:
Notation on comments: command-explanation -> (stack) // explanation
REEe # input -> (R)
REeeEeeeee # push space to last -> (R) // this prevents an infinite loop

rEE # create loop labeled 0 and automatically push 0
  re # pop 0 -> (r)
  REEEe # go to last item -> (R)
  REEEEEEe # ...then copy the char to other stack
  REEEE # go to first item -> (R)

  rEE # create loop labeled 32 // detect space
    REEEEEEE # move item to other stack (R)
  ree # do this while char != 32

  re # pop 32 -> (r)
  rEEEee # push item (dup to end) -> (r)
  re # ...then pop -> (r)
  rEEEe rEEeE # go to 2nd to last item -> (r)
  RrEEEee # push the item (R flag: dup to first) -> (r)
  rEEee # go to next -> (r) //
  re # ...then pop -> (r)
  reee rEEEEeEEE # out all as char then clear -> (r)
  ReEe # out 32 as char -> (R)
ree # do this while stack != 0

How does this work?

@lirtosiast explanation added

Python 3, 72 58 bytes
print(*[x[-1]+x[1:-1]+x[:x>x[0]]for x in input().split()])

Try it online!

Doesn't work for one letter words (eg a)

@TFeld, fixed..

J, 23 17 bytes
({:,1|.}:)&.>&.;:

Try it online!

Very nice trick to swap the first/last letters by rotating and applying 1 A. !

1&A.&.(1&|.) -> ({:,1|.}:) and then you can remove the ::]

Amazing, thank you

Really amazing! Once again I'm amazed how simple and elegant can the solution be, but only after I see it done by someone else.

laskelH, 71 bytes
h=reverse
s(x:y:o)=a:h(x:r)where(a:r)=h$y:o
s o=o
f=unwords.map s.words

Try it online!
Example in/output:
Swap the first and last letter in each word
This also works with single letter words like a

It is basically just a straight up implementation in which
I for words consisting of two or more letters cons the head
of the reversed tail on the reverse of the original head consed
on the reversed tail

Note that the rules say that we only have to support one kind
of separator - I am choosing spaces Technically it works with
other whitespace as well, but it will turn everything into spaces
in the end Line endings in this example usage are handled separately
to make the example output look nicer

pwaS eht tirsf dna tasl rettel ni hace dorw
shiT olsa sorkw hitw eingls rettel sordw eikl a

tI si yasicallb tusj a ttraighs pu nmplementatioi ni hhicw
I rof sordw gonsistinc fo owt ro eorm setterl sonc eht deah
fo eht deverser lait no eht eeversr fo eht lriginao deah donsec
no eht deverser lait

eotN that eht suler yas that ew ynlo eavh ot tuppors eno dink
fo reparatos - I ma ghoosinc spaces yechnicallT ti sorkw hitw
rtheo ehitespacw sa ,ellw tub ti lilw nurt gverythine onti spaces
ni eht dne einL sndinge ni shit example esagu era dandleh yeparatels
ot eakm eht example tutpuo kool ricen
```

The assignment in the where clause can be moved to a binding in guard to save 5 bytes: Try it online!

I see what you did there with the name "Haskell" in the title. I did the same thing on my PHP answer.

x86-16 machine code, IBM PC DOS, 39 38 32 bytes
$ xxd pwas.com
00000000: d1ee ac8a c8ac 8bd6 8bfe f2ae 574f 4f8a  ............WOO.
00000010: 25a4 4e88 245e 41e2 efb8 2409 aacd 21c3  %.N.$^A...$...!.

Unassembled:
D1 EE       SHR  SI, 1              ; DOS PSP (80H) for command line input
AC          LODSB                   ; get input length, SI at beginning
8A C8       MOV  CL, AL             ; input length in CX
AC          LODSB                   ; load a space (word delimiter) into AL
8B D6       MOV  DX, SI             ; save pointer for later output
        WORD_LOOP:
8B FE       MOV  DI, SI             ; start DI at beginning
F2 AE       REPNZ SCASB             ; search for next word delimiter
57          PUSH DI                 ; save beginning of next word
4F          DEC  DI                 ; DI will now point to the byte after the space
4F          DEC  DI                 ;  so it's necessary to back up two chars
8A 25       MOV  AH, BYTE PTR[DI]   ; move last letter to AH
A4          MOVSB                   ; move first letter to last position
4E          DEC  SI                 ; offset MOVSB incrementing SI
88 24       MOV  BYTE PTR[SI], AH   ; move last letter to first position
5E          POP  SI                 ; SI to beginning of next word
41          INC  CX                 ; offset LOOP decrementing CX
E2 EF       LOOP WORD_LOOP          ; loop if not end of input string
B8 0924     MOV  AX, 0924H          ; AH = 9, AL = '$'
AA          STOSB                   ; write DOS string delimiter to end
CD 21       INT  21H                ; write to console
C3          RET                     ; return to DOS

Standalone PC DOS executable.  Input via command line, output to console.

05AB1E, 10 bytes
#vyRćsRćðJ

Try it online!

-3 Thanks to @Kevin Cruijssen.
#           | Split into words.
 vy         | For each word...
   RćsRć    | Reverse, split head, swap, reverse, split tail
        ðJ  | Join by spaces.

10 bytes

@KevinCruijssen I honestly want to delete it and give it to you, that was 99% your brainpower on the ordering of the arguments haha.

The 100% brainpower only took a minute, so you can have it. ;p My initial approach was a map like this: #εRćsRćJ}ðý. And then I noticed your loop and thought that just pushing a space char and joining the entire stack would save a trailing }, hence the 10 byter. I have the feeling a 9-byter might be possible, though..

Found a 9-byter, but it only works in the legacy version: |ʒRćsRćJ,

Too bad we don't have a loop_as_long_as_there_are_inputs, then I would have known an 8-byter: [RćsRćJ, This 8-byter using [ never outputs in theory however, only when you're out of memory or time out like on TIO (and it requires a trailing newline in the input, otherwise it will keep using the last word)..

@KevinCruijssen I'll say the legacy version is a different answer entirely, it's Python 3 versus 2 if it doesn't work in both.

Unfortunately you need ð¡ as single word input is possible, but ð¡εćsÁì}ðý also works at 10 bytes.

@Emigna Oh, nice approach. Now that I see it I can't believe I hadn't thought about rotate.. >.> In that case it can be 8 bytes by using newlines as delimiter: |εćsÁì}» Or 7 bytes in the legacy.

@KevinCruijssen: The challenge states to take a string as input, so I don't think you can change that to taking each word as a separate input, so you'd have to add a byte to each of those versions. They still save a byte though (or 2 with legacy)

Yeah, #vyćsÁìðJ is 9 bytes and works with space separators. But OP has clarified that any non-alphabetic char is okay as the separator, so it's fine to go with |εćsÁì}»

Haskell, 54 bytes
unwords.map f.words
f[a]=[a]
f(a:b)=last b:init b++[a]

Try it online!

PowerShell, 37 bytes
$args-replace'(\w)(\w*)(\w)','$3$2$1'

Try it online!

Ruby with -p, 42 41 29 bytes
gsub /(\w)(\w*)(\w)/,'\3\2\1'

Try it online!

We no longer include flags in byte counts

@Shaggy thanks for the heads up. If you look at my post history it shows I was away for 8 months without any answers, so I've likely missed a few memos during that time, haha

Pretty sure the consensus was changed more than 8 months ago but just in case you missed it: "non-competing" is also no longer a thing.

Nicely done. I think under the rules you can use newlines as your delimiter and replace the \ws with .s.

Japt -S, 7 bytes
¸®ÎiZÅé

Try it

Nice! I knew there'd be a shorter way.

I am impressed. Well done

Vyxal, 7 bytes
⌈ƛḣǔp;Ṅ

Try it Online!
⌈     Ṅ # To the words split on spaces...
 ƛ   ;  # Over each
  ḣ     # Take the first character and the rest
   ǔ    # Rotate the rest
    p   # Append the first char

Ruby, 53 bytes
gets.split(" ").map{|z|print z[-1]+z[1..-2]+z[0]," "}

I tried it without regex. The output prints each word on a new line. If that's against the rules, let me know and I'll fix it.
Ungolfed:
gets.split(" ").map {|z|
    print z[-1] + z[1..-2] + z[0], " "
}

Welcome to PPCG! Printing each word on a new line should be fine, but your old solution of p was no good because that added quotes to the output. You could always use puts instead since that one auto-appends the newline and is shorter than print! Also, if you call split with no arguments it automatically splits on spaces.

Stax, 8 bytes
Σq╞♪áZN¢

Run and debug it
Uses newlines as word separators.

Wolfram Language (Mathematica), 58 bytes
StringReplace[#,a:u~~w:u..~~b:u:>b<>w<>a/.{u->Except@#2}]&

Try it online!
-22 bytes from @attinat
-12 bytes from @M.Stern

70 bytes using StringReplace with StringExpressions

64 bytes using StringTake instead of StringReplace: StringRiffle[StringSplit@##~StringTake~{{-1},{2,-2},{1}},#2,""]&

Here is a more direct approach: StringReplace[#,    a : u ~~ w : u .. ~~ b : u :> b <> w <> a /. {u -> Except@#2}] &

{ and } are optional :)

55 bytes, also fixes 2-character words

Whitespace, 179 bytes
[N
S S S N
_Create_Label_OUTER_LOOP][S S S N
_Push_n=0][N
S S T   N
_Create_Label_INNER_LOOP][S N
S _Duplicate_n][S N
S _Duplicate_n][S N
S _Duplicate_n][T   N
T   S _Read_STDIN_as_character][T   T   T   _Retrieve_input][S S S T    S T T   N
_Push_11][T S S T   _Subtract_t=input-11][N
T   T   S S N
_If_t<0_jump_to_Label_PRINT][S S S T    N
_Push_1][T  S S S _Add_n=n+1][N
S N
T   N
_Jump_to_Label_INNER_LOOP][N
S S S S N
_Create_Label_PRINT][S S S T    N
_Push_1][T  S S T   _Subtract_n=n-1][S N
S _Duplicate_n][S N
S _Duplicate_n][N
T   S N
_If_n==0_jump_to_Label_PRINT_TRAILING][T    T   T   _Retrieve][T    N
S S _Print_as_character][S S S N
_Push_s=0][N
S S S T N
_Create_Label_PRINT_LOOP][S S S T   N
_Push_1][T  S S S _Add_s=s+1][S N
S _Duplicate_s][S T S S T   S N
_Copy_0-based_2nd_n][T  S S T   _Subtract_i=s-n][N
T   S N
_If_0_Jump_to_Label_PRINT_TRAILING][S N
S _Duplicate_s][T   T   T   _Retrieve][T    N
S S _Print_as_character][N
S T S T N
_Jump_to_Label_PRINT_LOOP][N
S S N
_Create_Label_PRINT_TRAILING][S S S N
_Push_0][T  T   T   _Retrieve][T    N
S S _Print_as_character][S S S T    S S T   N
_Push_9_tab][T  N
S S _Print_as_character][N
S N
S N
_Jump_to_Label_OUTER_LOOP]

Letters S (space), T (tab), and N (new-line) added as highlighting only.
[..._some_action] added as explanation only.
Tab as delimiter. Input should contain a trailing newline (or tab), otherwise the program doesn't know when to stop, since taking input in Whitespace can only be done one character at a time.
Try it online (with raw spaces, tabs, and new-lines only).
Explanation in pseudo-code:
Whitespace only has a stack and a heap, where the heap is a map with a key and value (both integers). Inputs can only be read one integer or character at a time, which are always placed in the heap as integers, and can then be received and pushed to the stack with their defined heap-addresses (map-keys). In my approach I store the entire word at the heap-addresses (map-keys) \$[0, ..., \text{word_length}]\$, and then retrieve the characters to print one by one in the order we'd want after a tab (or newline) is encountered as delimiter.
Start OUTER_LOOP:
  Integer n = 0
  Start INNER_LOOP:
    Character c = STDIN as character, saved at heap-address n
    If(c == '\t' OR c == '\n'):
      Jump to PRINT
    n = n + 1
    Go to next iteration of INNER_LOOP

  PRINT:
    n = n - 1
    If(n == 0): (this means it was a single-letter word)
      Jump to PRINT_TRAILING
    Character c = get character from heap-address n
    Print c as character
    Integer s = 0

    Start PRINT_LOOP:
      s = s + 1
      If(s - n == 0):
        Jump to PRINT_TRAILING
      Character c = get character from heap-address s
      Print c as character
      Go to next iteration of PRINT_LOOP

    PRINT_TRAILING:
      Character c = get character from heap-address 0
      Print c as character
      Print '\t'
      Go to next iteration of OUTER_LOOP

The program terminates with an error when it tries to read a character when none is given in TIO (or it hangs waiting for an input in some Whitespace compilers like vii5ard).

Zsh, 36 bytes
for x;<<<$x[-1]${x:1:-1}$x[1]||<<<$x

Try it Online!

31 bytes

This fails for one-letter arguments ("a" → "aa").

Rust, 110 bytes
|a:&str|{for b in a.split(' '){let l=b.len()-1;if l>0{print!("{}{}",&b[l..],&b[1..l])}print!("{} ",&b[0..1])}}

Try it online!
A surprisingly long, but nicer solution that returns instead of printing (126 bytes):
|a:&str|->String{a.split(' ').flat_map(|b|{let mut q:Vec<_>=b.chars().collect();q.swap(0,b.len()-1);q.push(' ');q}).collect()}

Try it online!

sed, 34 bytes
And presumably the pattern idea will work with most RE tools (and I do know there are differences between standard RE and extended RE).
s,\b\(\w\)\(\w*\)\(\w\)\b,\3\2\1,g

Try it online!

Welcome to PPCG! Methinks that the greedy nature of regex matches means you can cut the \b from the match: Try it online!

Agreed @ValueInk -- but I was being accurate with the match.  Removing the \b will lead to 30 bytes.

Use -E for advanced regex and you can use unescaped parentheses. Use . for the swapped chars and you can lose another two chars. This brings yours down to 26 bytes; one of the smallest readable solutions. s,\b(.)(\w*)(.)\b,\3\2\1,g

nope, I'm wrong, you need the \ws at the ends. s,\b(\w)(\w*)(\w)\b,\3\2\1,g 28 chars.

Nice work @rich, but as I said I know about standard and extended RE.  I just chose to make it standard and readable. The anchors would be required which I neglected to mention in my reply to ValueInk.

@Rich, ValueInk got me thinking and checking.  Based on the test case results, you CAN drop the \b anchors provided you use word characters (\w) -- resulting in 25 bytes. (Note: to get 25 bytes you need to use sed -E to avoid escaped parentheses)

QuadR, 20 bytes
(\w)(\w*)(\w)
\3\2\1

Simply make three capturing groups consisting of 1, 0-or-more, and 1 word-characters, then reverses their order.
Try it online!

Perl 5 -p, 24 bytes
s/(\w)(\w*)(\w)/$3$2$1/g

Try it online!

You can get it to 21 by using the newline as separator: Try it online!

Nice one! Came up with similar, but necessitates adding -040l flags for 19: Try it online!

Batch, 141 bytes
@set t=
@for %%w in (%*)do @call:c %%w
@echo%t%
@exit/b
:c
@set s=%1
@if not %s%==%s:~,1% set s=%s:~-1%%s:~1,-1%%s:~,1%
@set t=%t% %s%

Takes input as command-line parameters. String manipulation is dire in Batch at best, and having to special-case single-letter words doesn't help.

APL+WIN, 50 bytes
(∊¯1↑¨s),¨1↓¨(¯1↓¨s),¨↑¨s←((+\s=' ')⊂s←' ',⎕)~¨' '

Prompts for string and uses space as the delimiter.
Try it online! Courtesy of Dyalog Classic

sed, 64 bytes
sed -E 's/\b([[:alpha:]])([[:alpha:]]*)([[:alpha:]])\b/\3\2\1/g'

Sure, we could use . instead of [[:alpha:]], but it would actually have to be [^ ], which reduces it to 43, but breaks on punctuation and such. Using [a-zA-Z] brings it up to 55, by which point I'm just hankering after those sweet, sweet human readable entities...

You do not need to handle punctuation; all of the inputs will only consist of the letters a through z, separated by a delimiter, all of a uniform case. In other words, you don't need to worry about punctuation "breaking" your code and can just safely go for [^ ] ;)

@ValueInk Yeah, but then [^ ] should be [^[:space:]] which brings it to 67 chars.

"a delimiter" means you can ensure that the delimiter is always a regular space. Who uses tabs in a sentence anyways??

Okay. Seems like "code golf" is a game where you're meant to find ways to get a drone to drop the ball in the hole instead of actually doing the work. Thanks for the shitty welcome.

Sorry about that... and yes, you could look at "code golf" as a game where you find ways to build as small of a drone as possible to drop the ball in the hole. We try to be friendly and give advice on how to make your drone smaller, but if I've been too overbearing then I apologize :(

Thing is, the point of the exercise is to learn optimization. It's suboptimal if nobody can read it. Some people are Trump and will lie and cheat and steal a child's ball; others are there for the game.

Anyway, I think I'll play in my corner. It seems like fun. I'll just have to learn to not respond.

Julia 1.0, 75 bytes
x->join([(n=length(s);n<2 ? s : s[n]*s[2:n-1]*s[1]) for s in split(x)]," ")

Try it online!

Vyxal S, 31 bitsv2, 3.875 bytes
⌈ƛḣǔp

Try it Online!
Ports Thunno 2.
Explained
⌈ƛḣǔp
⌈ƛ     # To each word after splitting on spaces
  ḣ    #   Push the first character, then the rest
   ǔ   #   Rotate right once
    p  #   Prepend to the first character
# The S flag joins on spaces.

Pip, 15 bytes
aR+XW{@a::a@va}

Takes the string as a command-line argument. Try it here! Or, here's a 14-byte version in Pip Classic: Try it online!
Explanation
aR+XW{@a::a@va}
a                In the command-line argument,
 R               replace all matches of
  +XW            the regex `\w+`
     {        }  with the following callback function:
      @a          Take the first character of the matched string
          a@v     and its -1th (last) character
        ::        and swap them
             a    Return the modified string

Husk, 10 bytes
wm§:→oṙ1hw

Try it online!
Explanation
wm§:→oṙ1hw
 m       w  map over each word
  §:        concatenate the
    →       last char
     o  h   and the rest
      ṙ1    rotated
w           join on spaces

Nibbles, 5 bytes (10 nibbles)
:`(\@:`)\$

Word separator is newline character.
:`(\@:`)\$$ # (code with implicitly-added final $)
            # implicit map over lines of input:
:           # joined
 `(         # first character of
   \@       # reversed word
            # (and save leftover of reversed word in variable $)
            # with
     :      # joined
      `)    # all but first character of
        \$  # reversed leftover of reversed word
            # with
          $ # first character of leftover of reversed word
            # (so, first character of the original word)

><> (Fish), 80 78 bytes
3:i&1+:i:'!'(?v$4p30.
$&p4{g4:-1v?(0/p4
. 1ap31'v'/
|.!00+1p4$' ':/
"/"
o>:?!;

Saved 2 bytes trying to fix handling 1-letter words
Try it

Thunno 2 Ṡ, 6 bytes
OıḢsṾƤ

Attempt This Online!
Explanation
OıḢsṾƤ  # Implicit input
O       # Split on spaces
 ı      # Map over the list:
  Ḣ     #  Extract the first character
   s    #  Swap so the rest is on top
    Ṿ   #  Rotate it right once
     Ƥ  #  Append the first character
        # Join the list by spaces
        # Implicit output

Arturo, 37 bytes
$=>[replace&{/(\w)(\w*)(\w)}"$3$2$1"]

Try it!

JavaScript (Node.js), 72 bytes
Solution without regex = two times longer!
s=>s.split` `.map(w=>w[w.length-1]+w.slice(1,-1)+(w[1]?w[0]:"")).join` `

Try it online!

More functional programming approach (81 bytes) :
s=>s.split` `.map(w=>[...w].map((c,i)=>i%(l=w.length-1)?c:w[l-i]).join``).join` `

s.split\ `.map(...).join` `can bes.replace(/\S+/g,...)(technically using RegEx but not for the hard part of the challenge),w[w.length-1]can bew.at(-1)(though.at()` is not available on TIO since the site hasn't been updated in years and still uses Node.js 11; I usually use Attempt This Online! but it is currently offline)

@noodleman Oh! Thank you for the tips! I'll see if i can find a reliable and convenient way for me to use newer versions of JS :) Regarding replace, i'm not very fond because it uses regex which i try to not use in this solution. Without this self-imposed-constraint i would be expected to do way much shorter (regarding others' solutions) and this would be less fun for me because i prefer functional algorithms :/

Brainf**k, 126
>+[,[->>>+<+<+<]>----------[[-]<+>]>[[-]<<+>>]<<--[+<.<[[<]>>>[<.>>]<[<]>.<]>[>]>->>[.[-]<+>]<<<]>+[->>[-<<<+>>>]<+<]>[-<+>]<]

Try it online!
Could definitely be golfed further.

Python 2, 67 66 64 61 60 bytes
lambda s:' '.join(w[1:][-1:]+w[1:-1]+w[0]for w in s.split())

Try it online!
-1 byte, thanks to squid
-1 byte, thanks to Erik the Outgolfer

Python 3, 63 61 58 57 bytes
print(*[w[1:][-1:]+w[1:-1]+w[0]for w in input().split()])

Try it online!

You can remove the whitespace before ">"

@squid thanks. I'm on mobile, so whitespace is hard on tio.

Your 58-byte version is now an exact (except for the variable name) copy of alexz02's latest edit from 20 minutes ago

@kevincruijssen ergh.. To be fair, that answer is a trivial golf of my earlier edit...

True. I was mentioning it merely as a FYI. :) There isn't any rule disqualifying duplicated answers when both users independently came to the same solutions.

The way you've chosen to account for 1-letter words might prevent you from seeing that this also works, since getting there involves two steps: 1) replace w[:w>w[0]] with w[:-1][:1]; this works because it firstly removes the last character, and, if the word only has one letter, the last character is also the first 2) you then move the mechanism to the other side, so that you have a [0] instead of a [-1].

@eriktheoutgolfer thanks, :) that's clever

PHP, 73 bytes
foreach(explode(' ',$argn)as$w){[$w[0],$w[-1]]=[$w[-1],$w[0]];echo"$w ";}

Try it online!
Using PHP 7.1's Square bracket syntax for array destructuring to swap values.
Ungolfed:
foreach( explode( ' ', $argn ) as $w ) {
  [ $w[0], $w[-1] ] = [ $w[-1], $w[0] ];
  echo $w, ' ';
}

Japt -S, 10 bytes
Convinced there has to be a shorter approach (and I was right) but this'll do for now.
¸ËhJDg)hDÌ

Try it
¸ËhJDg)hDÌ     :Implicit input of string
¸              :Split on spaces
 Ë             :Map each D
  h            :  Set the character at
   J           :    Index -1 to
    Dg         :    The first character in D
      )        :  End set
       h       :  Set the first character to
        DÌ     :    The last character in D
               :Implicit output, joined by spaces

Much shorter than my 12 byter: ¸®Ì+Zs1J +Zg

@EmbodimentofIgnorance, that's where I started, too, but it would have failed on single character words. You could save a byte on that, though, with ¸®ÎiZÌ+Zs1J.

@EmbodimentofIgnorance Found a 7 byter

@Downvoter, why?

Java, 110 109 bytes
-1 bytes by using a newline for a delimeter
s->{int l;for(var i:s.split("\n"))System.out.println(i.charAt(l=i.length()-1)+i.substring(1,l)+i.charAt(0));}

TIO

Does this work for single-letter words?

@Neil no because I'm bad. I'll fix later.

109 by using newline as delimiter

Jelly, 9 bytes
ḲṪ;ṙ1$Ɗ€K

Try it online!
Explanation
ḲṪ;ṙ1$Ɗ€K | monadic link taking the string as input

Ḳ         | split at spaces
      Ɗ€  | for each word, do the following:
 Ṫ        | - pop the last letter
  ;ṙ1$    | - concatenate to the remaining letters rotated left once
        K | finally, join with spaces

C# (Visual C# Interactive Compiler), 90 bytes
n=>n.Split().Any(x=>WriteLine(x.Length<2?x:x.Last()+x.Substring(1,x.Length-2)+x[0])is int)

Uses newline as delimiter, though really any whitespace can be used.
Try it online!

SelectMany (that is, map and flatten) for 84 bytes, but outputs a single trailing space. Try it online!

Icon, 76 bytes
link segment
procedure f(s)
w:=!seglist(s,' ')&w[1]:=:w[-1]&writes(w)&\x
end

Try it online!

C (gcc), 62 bytes
t;*s;f(int*i){for(s=i;*++i;i[1]>64||(t=*i,*i=*s,*s=t,s=i+2));}

I wanted to use a xor swap but that fails if a word is only one character long.
Try it online

Suggest i[1]<65?t=*i,*i=*s,*s=t,s=i+2:0 instead of i[1]>64||(t=*i,*i=*s,*s=t,s=i+2)

Haskell, 75 74 bytes
Fixed a bug pointed at by Cubic and also golfed down 1 byte.
f=unwords.map(#v).words
x#g=g(r$tail x)++[x!!0]
r=reverse
v[]=[]
v x=r$x#r

Try it online!

map g is shorter than (g<$>)

Also, if you look at your test case you'll see it doesn't work for one letter words, it turns a into aa

Scala, 100 bytes
(b:String,c:String)=>b.split(c)map(f=>f.tail.lastOption++:(f.drop(1).dropRight(1)+f.head))mkString c

T-SQL, 126 bytes
SELECT STRING_AGG(STUFF(STUFF(value,1,1,RIGHT(value,1)),LEN(value),1,LEFT(value,1)),' ')
FROM STRING_SPLIT((SELECT*FROM t),' ')

Input is via a pre-existing table t with varchar field v, per our IO standards.
Reading from back to front, STRING_SPLIT breaks a string into individual rows via a delimiter, STUFF modifies the characters at the specified positions, then STRING_AGG mashes them back together again.

SNOBOL4 (CSNOBOL4), 90 bytes
I	R =
	INPUT LEN(1) . L REM . M	:F(END)
	M ARB . M RPOS(1) REM . R
	OUTPUT =R M L	:(I)
END

Try it online!
Takes input separated by newlines; can be either uppercase or lowercase.
I R =					;* set R to empty string
  INPUT LEN(1) . L REM . M	:F(END)	;* take first character and set to L, and set the
					;* REMainder to M
  M ARB . M RPOS(1) REM . R		;* match an ARBitrary (possibly empty) run
					;* of characters to M up to but excluding the last character
					;* and save the last character to R
					;* if M is empty, (i.e., a one-letter word), then this fails
					;* and nothing happens, so M remains empty and R remains empty
  OUTPUT =R M L	:(I)			;* output Right, Middle, Left, then goto I.
END

(previous version)
SNOBOL4 (CSNOBOL4), 92 bytes
I	R =M =
	INPUT LEN(1) . L ('' | ARB . M LEN(1) . R) RPOS(0)	:F(END)
	OUTPUT =R M L	:(I)
END

Try it online!
This is thematically the same, clearly, but suffers from using FAILURE as the termination status, preventing us from using FAILURE as a no-op as we do in the above. This then forces us to set M = as well as R =, which is 3 bytes.

PowerShell, 50 bytes
-split"$args"|%{$_-replace'^(.)(.*)(.)$','$3$2$1'}

Try it online!
Uses regex to replace each word with a captured first and last letter surrounding the original core. If it's a single character, replace will find nothing and leave it alone.

Z80Golf, 43 bytes
00000000: 2100 c0cd 0380 3822 5745 cd03 8038 09fe  !.....8"WE...8..
00000010: 2028 0570 4723 18f2 736b 707e 23a7 2803   (.pG#..skp~#.(.
00000020: ff18 f87a ff3e 20ff 18d6 76              ...z.> ...v

Try it online!
Corresponding assembly:
mainloop:
    ld hl, $c000
    call $8003
    jr c, hlt
    ld d, a
    ld b, l ; handle 1-character words
inputloop:
    call $8003
    jr c, endinput
    cp $20
    jr z, endinput
    ld (hl), b ; don't store the last character
    ld b, a
    inc hl
    jr inputloop
endinput:
    ld (hl), e ; always 0
    ld l, e
    ld (hl), b
outputloop:
    ld a, (hl)
    inc hl
    and a
    jr z, endoutput
    rst $38
    jr outputloop
endoutput:
    ld a, d
    rst $38
    ld a, $20
    rst $38
    jr mainloop
hlt:
    halt

If halting is not necessary:
Aheui (esotope), 174 bytes
삭붵뱷뛰빠쇡붷뼤쎄투＠싼사쑫
ＢｙＬｅ쪼ｇｅｎ＠처쇠모코커
ＤＵＳＴ오멓＠＠＠＠푸셴쒼섣
＠＠＠＠쇡뽀＠＠＠삳멓＠샨＠맣

Try it online!
press 'start' butten in TIO again to halt manually.
If halting is necessary:
Aheui (esotope), 225 bytes
살뷕볙눠쀄삭붵뱷뛰빠쇡붷빠쎄투＠싼사쑫
Ｂｙ＠＠＠＠＠야빠속＠＠＠수처쇠모오어
Ｌｅｇｅｎ＠＠먷초더헤셜썰뻐푸쉰썬＠셛
ＤＵＳＴ＠＠솩뽀사뫼섁쀠우삳멓산멓

Try it online!
In this version, following space after input is necessary. fixed. Now it is OK to not finish with space.

brainfuck, 71 67 49 47 bytes
+[-[+>>]>[<<.[-]<[<]>>[.>]<[<]>[.[>]].>>>]<+<,]

Try it online!
This code uses a few cheats, so i don't know if it is competing. The separator in the input is a 0x01. The input needs an extra trailing separator, otherwise the last word won't be printed.
code:
+[                  enter the loop / the first round only the last three commands of the main loop are interesting
  -[                if input is not 0x01
    +               restore character
    >>              go to exit if
  ]
  >[                else
    <<.[-]          print and delete last character
    <[<]>>[.>]      print all characters starting at the second
    <[<]>[.[>]]     print first character and go to end if word is longer than one character
    .               print null (space)
    >>>             leave a zero cell and go to exit else
  ]
  <+                set new else marker
  <                 go to new input location
  ,                 input next character
]

Factor, 60 bytes
[ " "split [ 1 cut 1 short cut* spin 3append ] map " "join ]

This doesn't run on TIO (Try it Online) because spin postdates build 1525, the one TIO uses. Here's a screenshot of running it in build 1889, the official 0.98 release:

Explanation
It's a quotation (anonymous function) that takes a string from the data stack as input and leaves a string on the data stack as output. Assuming "in a green meadow" is on top of the data stack when this quotation is called...

Snippet
Comment
Data stack (the bottom is the top)

" "split
Split a string into a sequence of space-delimited strings
{ "in" "a" "green" "meadow" }

[ ... ] map
Apply a quotation to each element of a sequence, collecting the results in a new sequence of the same length

Inside the quotation during the first iteration of map now...
"in"

1 cut
Split a string in two at index 1
"i""n"

1 short
The minimum of 1 and the length of the sequence on top of the stack — prevents cut* from trying to cut at an invalid index
"i""n"1

cut*
Like cut, but works from the end of the sequence; not the start
"i""""n"

spin
Swap the object on top of the data stack with the third object from the top of the data stack
"n""""i"

3append
Append three sequences
"ni"

And so forth...
{ "ni" "a" "nreeg" "weadom" }

" "join
Join a sequence of strings into a single string separated by a space
"ni a nreeg weadom"

[ " "split [ 1 cut -1 rotate prepend ] map " "join ] for 52 bytes with sequences.extras seems to work. I've never used Factor before but I've been meaning to try it out, code-golf seems like a good place to start

@noodleman Unfortunately this crashes on inputs that have a single-letter word, since rotating an empty sequence causes an error. Glad to hear you're interested! It would be fantastic to have some competition in Factor. :) Feel free to join the Factor Discord -- I am happy to answer questions.

Stax, 6 bytes
╡à⌂≤¬)

Run and debug it
Uses newline for the word delimiter

Lexurgy, 30 bytes
s:
[]$1 ([]*)$2 []$3=>$3 $2 $1

GolfScript, 19 15 bytes
-4 bytes by splitting on newlines
Input is taken seperated by newlines.
n/{[)\(]''+}%n*

Try it online!
Explanation
n/{[)\(]''+}%n* # whole program
n/              # split the <implicit> input by newlines
    {        }% # for each value in the split input
     [)  ]      # take the last item of the string
       \        # swap stack ; stack: <last letter> <word without last letter>
        (       # take the first item out of the string ; stack: <last letter> <word> <first letter>
          ''+    # concatenate into charcodes
              n* # join by newlines

jq, 73 68 bytes
./" "|map((length<2//[./""|.[-1],.[1:-1][],.[0]]|add?)//.)|join(" ")

Try it online!
How?
  . / " "                             # Split on spaces
| map(                                # For each word...
    (
         length<2                     # If it's shorter than 2 letters, emit false
      // [   . / ""                   # Otherwise, emit the word's characters
           | .[-1], .[1:-1][], .[0]]  # ...in the order last, middle, first
      | add?                          # Then re-join the characters into an updated word. If we don't have a list of characters, (if the original was too short), emit nothing
    ) // .                            # If we didn't get a value, emit the word unchanged
  )
| join(" ")


text:
Save my secrets!
I have so many secrets and nowhere to keep them!
The goal is simple: write a program that lets you save a string and have it be protected by a password.
The program will take an alias, password and (optional) secret as parameters.
If this is the first time the program is called with a given alias, then it will store/remember the secret, and output whatever you want.
If the program is called with an alias that has been used before, then it will output the secret for that alias iff the password is the same as the first time the program was run with that alias.
Other cases

If its the first time being called with a certain alias and no secret is given - store nothing.
If the program is called with an alias that has been used, and the password is incorrect - return any kind of error or nothing at all.
If the program is called with an alias that has been used, the password is correct and a new secret is supplied - output the old secret and replace it with the new one so that next time only the new secret is output.
If the program is called with an alias that has been used, the password is correct and no new secret is supplied - output the old secret and make sure it doesn't get replaced.

Note: These secrets/passwords do not need to be stored securely
Also note: any alphanumeric input for aliases passwords and secrets should be accepted
Standard rules apply, good luck!

What kind of storage may we use? Anything?

May we limit the valid aliases to say lowercase alphabetical words?

Yes, use anything to store it - as for aliases I think it should accept any alphanumeric input

Updated question to specify

May we use a function rather than a full program?

@Arnauld I'll allow it

May we assume the password and secret are not the empty string?

May we output a null answer (like 0 or None) in any situation where we don't output the secret?

@xnor I will say yes to both those questions

May we output different falsy values (like false and undefined) when there's no secret to output?

@Arnauld yes you may

JavaScript (ES6),  60  50 bytes
Saved 10 bytes thanks to @JonasWilms!
Takes input as either (alias,password,secret) or (alias,password). Returns undefined the first time a secret is stored, or false if the password is incorrect.
f=(a,p,s,[P,v]=f[a]||[p])=>p==P&&(f[a]=[P,s||v],v)

Try a 1st test case online!
Try a 2nd test case online!
How?
We define a named function \$f\$ whose underlying object is also used to store the passwords and the secrets.
Commented
f = (             // f = named function whose underlying object is used for storage
  a,              // a = alias
  p,              // p = password
  s,              // s = optional secret
  [P, v] = f[a]   // if f[a] is defined, retrieve the password P and the secret v
           || [p] // otherwise, copy p in P
) =>              //
  p == P && (     // if p is not equal to P, yield false; else:
    f[a] = [      //   update f[a]:
      P,          //     save the new password (or leave it unchanged)
      s || v      //     save the new secret if it's defined, or keep the previous one
    ],            //
    v             //   return the previous secret
  )               //

Took me a second to spot how you were storing things!

hmm, is this correct?

@ngn It's most likely incorrect indeed. Thanks for reporting this. Should be fixed now.

-7 bytes through storing a [password, value] array inside of the object.

@JonasWilms Nicely done! I can't think of any situation where p&& is required. So, that's another -3 bytes.

@arnauld ah right, missread one of the cases. Thanks :)

Python 2, 94 93 bytes
def f(a,p,s=0,d={}):
 q,t=d.get(a,(0,0))
 if q==p:d[a]=p,s or t;return t
 elif q<1<s:d[a]=p,s

Try it online!
For once, Python's weird default dict parameter works in my favor...

Wait, using a default argument makes it just one object instead of a new one on function call? Hmmm... This explains a lot of debugging I've had to do previously lol.

Ruby, 64 bytes
Builds a hash for aliases to a single key-pair of password => secret. Probably could be more elegant.
->a,w,s=p{@q||={};(b=@q[a])?s&&b[w]?b[w]=s:b[w]:s&&@q[a]={w=>s}}

Try it online!

Wolfram Language (Mathematica), 34 bytes
(c=#~g~#2;#3!=##3||(#~g~#2=#3);c)&

Try it online!

C# (Visual C# Interactive Compiler), 140 138 134 bytes
-2 bytes thanks to @Expired Data

a=>p=>s=>(a=P.ContainsKey(a)?P[a]:P[a]=new[]{p,s},o:p==a[0]?a[1]:p,s==""?s:p==a[0]?a[1]=s:s).o;var P=new Dictionary<string,string[]>()

Try it online!

138 bytes

@ExpiredData 134, as P doesn't have to be dynamic now

130 bytes using the same trick again

Python 2, 77 bytes
def f(a,p,s=0,d={}):
 P,S=d.get(a,(p,0))
 if p==P:
	if s>0:d[a]=p,s
	return S

Try it online!
Similar to Chas Brown's method.

source: Stack Exchange

license: Creative Commons - Attribution Share-Alike - https://creativecommons.org/licenses/by-sa/4.0/

sort: votes


text:
Warped chessboard
This challenge is about building a chessboard in which the square size, instead of being constant across the board, follows a certain non-decreasing sequence, as described below.
The board is defined iteratively. A board of size \$n \times n\$ is enlarged to size \$(n+k)\times(n+k)\$ by extending it down and to the right by a "layer" of squares of size \$k\$, where \$k\$ is the greatest divisor of \$n\$ not exceeding \$\sqrt{n}\$. The squares in the diagonal are always of the same colour.
Specifically, consider the board with colours represented as # and +.

Initialize the chessboard to
#

The board so far has size \$1\times 1\$. The only divisor of \$1\$ is \$1\$, and it does not exceed \$\sqrt{1}\$. So we take \$k=1\$, and extend the board by adding a layer of squares of size \$1\$, with # in the diagonal:
#+
+#

The board built so far has size \$2 \times 2\$. The divisors of \$2\$ are \$1,2\$, and the maximum divisor not exceeding \$\sqrt{2}\$ is \$1\$. So again \$k=1\$, and the board is extended to
#+#
+#+
#+#

Size is \$3 \times 3\$. \$k=1\$. Extend to
#+#+
+#+#
#+#+
+#+#

Size is \$4 \times 4\$. Now \$k=2\$, because \$2\$ is the maximum divisor of \$4\$ not exceeding \$\sqrt 4\$. Extend with a layer of thickness \$2\$, formed by squares of size \$2\times 2\$, with colour # in the diagonal:
#+#+##
+#+###
#+#+++
+#+#++
##++##
##++##

Size is \$6 \times 6\$. Now \$k=2\$. Extend to size \$8 \times 8\$. Now \$k=2\$. Extend to size \$10 \times 10\$. Now \$k=2\$. Extend to size \$12 \times 12\$. Now \$k=3\$. Extend to size \$15\$:
#+#+##++##++###
+#+###++##++###
#+#+++##++#####
+#+#++##++##+++
##++##++##+++++
##++##++##+++++
++##++##++#####
++##++##++#####
##++##++##++###
##++##++##+++++
++##++##++##+++
++##++##++##+++
###+++###+++###
###+++###+++###
###+++###+++###

Note how the most recently added squares, of size \$3 \times 3\$, have sides that partially coincide with those of the previously added squares of size \$ 2 \times 2 \$.
The sequence formed by the values of \$k\$ is non-decreasing:
1 1 1 2 2 2 2 3 3 3 3 4 4 4 6 6 6 6 6 6 ...

and does not seem to be in OEIS. However, its cumulative version, which is the sequence of sizes of the board, is A139542 (thanks to @Arnauld for noticing).
The challenge
Input: a positive integer \$S\$ representing the number of layers in the board. If you prefer, you may also get \$S-1\$ instead of \$S\$ as input (\$0\$-indexed); see below.
Output: an ASCII-art representation of a board with \$S\$ layers.

Output may be through STDOUT or an argument returned by a function. In this case it may be a string with newlines, a 2D character array or an array of strings.
You can consistently choose any two characters for representing the board.
You can consistently choose the direction of growth. That is, instead of the above representations (which grow downward and rightward), you can produce any of its reflected or rotated versions.
Trailing or leading space is allowed (if output is through STDOUT), as long as space is not one of the two characters used for the board.
You can optionally use "\$0\$-indexed" input; that is, take as input \$S-1\$, which specifies a board with \$S\$ layers.

Shortest code in bytes wins.
Test cases
1:
#

3:
#+#
+#+
#+#

5:
#+#+##
+#+###
#+#+++
+#+#++
##++##
##++##

6:
#+#+##++
+#+###++
#+#+++##
+#+#++##
##++##++
##++##++
++##++##
++##++##

10:
#+#+##++##++###+++
+#+###++##++###+++
#+#+++##++#####+++
+#+#++##++##+++###
##++##++##+++++###
##++##++##+++++###
++##++##++#####+++
++##++##++#####+++
##++##++##++###+++
##++##++##+++++###
++##++##++##+++###
++##++##++##+++###
###+++###+++###+++
###+++###+++###+++
###+++###+++###+++
+++###+++###+++###
+++###+++###+++###
+++###+++###+++###

15:
#+#+##++##++###+++###+++####++++####
+#+###++##++###+++###+++####++++####
#+#+++##++#####+++###+++####++++####
+#+#++##++##+++###+++#######++++####
##++##++##+++++###+++###++++####++++
##++##++##+++++###+++###++++####++++
++##++##++#####+++###+++++++####++++
++##++##++#####+++###+++++++####++++
##++##++##++###+++###+++####++++####
##++##++##+++++###+++#######++++####
++##++##++##+++###+++#######++++####
++##++##++##+++###+++#######++++####
###+++###+++###+++###+++++++####++++
###+++###+++###+++###+++++++####++++
###+++###+++###+++###+++++++####++++
+++###+++###+++###+++###++++####++++
+++###+++###+++###+++#######++++####
+++###+++###+++###+++#######++++####
###+++###+++###+++###+++####++++####
###+++###+++###+++###+++####++++####
###+++###+++###+++###+++++++####++++
+++###+++###+++###+++###++++####++++
+++###+++###+++###+++###++++####++++
+++###+++###+++###+++###++++####++++
####++++####++++####++++####++++####
####++++####++++####++++####++++####
####++++####++++####++++####++++####
####++++####++++####++++####++++####
++++####++++####++++####++++####++++
++++####++++####++++####++++####++++
++++####++++####++++####++++####++++
++++####++++####++++####++++####++++
####++++####++++####++++####++++####
####++++####++++####++++####++++####
####++++####++++####++++####++++####
####++++####++++####++++####++++####

25:
#+#+##++##++###+++###+++####++++##########++++++######++++++######++++++++++++++########++++++++########++++++++########
+#+###++##++###+++###+++####++++##########++++++######++++++######++++++++++++++########++++++++########++++++++########
#+#+++##++#####+++###+++####++++##########++++++######++++++######++++++++++++++########++++++++########++++++++########
+#+#++##++##+++###+++#######++++##########++++++######++++++######++++++++++++++########++++++++########++++++++########
##++##++##+++++###+++###++++####++++######++++++######++++++######++++++++++++++########++++++++########++++++++########
##++##++##+++++###+++###++++####++++######++++++######++++++######++++++++++++++########++++++++########++++++++########
++##++##++#####+++###+++++++####++++++++++######++++++######++++++######++++++++########++++++++########++++++++########
++##++##++#####+++###+++++++####++++++++++######++++++######++++++######++++++++########++++++++########++++++++########
##++##++##++###+++###+++####++++####++++++######++++++######++++++##############++++++++########++++++++########++++++++
##++##++##+++++###+++#######++++####++++++######++++++######++++++##############++++++++########++++++++########++++++++
++##++##++##+++###+++#######++++####++++++######++++++######++++++##############++++++++########++++++++########++++++++
++##++##++##+++###+++#######++++####++++++######++++++######++++++##############++++++++########++++++++########++++++++
###+++###+++###+++###+++++++####++++######++++++######++++++######++++++########++++++++########++++++++########++++++++
###+++###+++###+++###+++++++####++++######++++++######++++++######++++++########++++++++########++++++++########++++++++
###+++###+++###+++###+++++++####++++######++++++######++++++######++++++########++++++++########++++++++########++++++++
+++###+++###+++###+++###++++####++++######++++++######++++++######++++++########++++++++########++++++++########++++++++
+++###+++###+++###+++#######++++##########++++++######++++++######++++++++++++++########++++++++########++++++++########
+++###+++###+++###+++#######++++##########++++++######++++++######++++++++++++++########++++++++########++++++++########
###+++###+++###+++###+++####++++####++++++######++++++######++++++######++++++++########++++++++########++++++++########
###+++###+++###+++###+++####++++####++++++######++++++######++++++######++++++++########++++++++########++++++++########
###+++###+++###+++###+++++++####++++++++++######++++++######++++++######++++++++########++++++++########++++++++########
+++###+++###+++###+++###++++####++++++++++######++++++######++++++######++++++++########++++++++########++++++++########
+++###+++###+++###+++###++++####++++++++++######++++++######++++++######++++++++########++++++++########++++++++########
+++###+++###+++###+++###++++####++++++++++######++++++######++++++######++++++++########++++++++########++++++++########
####++++####++++####++++####++++##########++++++######++++++######++++++########++++++++########++++++++########++++++++
####++++####++++####++++####++++##########++++++######++++++######++++++########++++++++########++++++++########++++++++
####++++####++++####++++####++++##########++++++######++++++######++++++########++++++++########++++++++########++++++++
####++++####++++####++++####++++##########++++++######++++++######++++++########++++++++########++++++++########++++++++
++++####++++####++++####++++####++++######++++++######++++++######++++++########++++++++########++++++++########++++++++
++++####++++####++++####++++####++++######++++++######++++++######++++++########++++++++########++++++++########++++++++
++++####++++####++++####++++####++++++++++######++++++######++++++##############++++++++########++++++++########++++++++
++++####++++####++++####++++####++++++++++######++++++######++++++##############++++++++########++++++++########++++++++
####++++####++++####++++####++++####++++++######++++++######++++++######++++++++########++++++++########++++++++########
####++++####++++####++++####++++####++++++######++++++######++++++######++++++++########++++++++########++++++++########
####++++####++++####++++####++++####++++++######++++++######++++++######++++++++########++++++++########++++++++########
####++++####++++####++++####++++####++++++######++++++######++++++######++++++++########++++++++########++++++++########
######++++++######++++++######++++++######++++++######++++++######++++++++++++++########++++++++########++++++++########
######++++++######++++++######++++++######++++++######++++++######++++++++++++++########++++++++########++++++++########
######++++++######++++++######++++++######++++++######++++++######++++++++++++++########++++++++########++++++++########
######++++++######++++++######++++++######++++++######++++++######++++++++++++++########++++++++########++++++++########
######++++++######++++++######++++++######++++++######++++++######++++++########++++++++########++++++++########++++++++
######++++++######++++++######++++++######++++++######++++++######++++++########++++++++########++++++++########++++++++
++++++######++++++######++++++######++++++######++++++######++++++##############++++++++########++++++++########++++++++
++++++######++++++######++++++######++++++######++++++######++++++##############++++++++########++++++++########++++++++
++++++######++++++######++++++######++++++######++++++######++++++##############++++++++########++++++++########++++++++
++++++######++++++######++++++######++++++######++++++######++++++##############++++++++########++++++++########++++++++
++++++######++++++######++++++######++++++######++++++######++++++##############++++++++########++++++++########++++++++
++++++######++++++######++++++######++++++######++++++######++++++##############++++++++########++++++++########++++++++
######++++++######++++++######++++++######++++++######++++++######++++++++++++++########++++++++########++++++++########
######++++++######++++++######++++++######++++++######++++++######++++++++++++++########++++++++########++++++++########
######++++++######++++++######++++++######++++++######++++++######++++++++++++++########++++++++########++++++++########
######++++++######++++++######++++++######++++++######++++++######++++++++++++++########++++++++########++++++++########
######++++++######++++++######++++++######++++++######++++++######++++++++++++++########++++++++########++++++++########
######++++++######++++++######++++++######++++++######++++++######++++++++++++++########++++++++########++++++++########
++++++######++++++######++++++######++++++######++++++######++++++######++++++++########++++++++########++++++++########
++++++######++++++######++++++######++++++######++++++######++++++######++++++++########++++++++########++++++++########
++++++######++++++######++++++######++++++######++++++######++++++##############++++++++########++++++++########++++++++
++++++######++++++######++++++######++++++######++++++######++++++##############++++++++########++++++++########++++++++
++++++######++++++######++++++######++++++######++++++######++++++##############++++++++########++++++++########++++++++
++++++######++++++######++++++######++++++######++++++######++++++##############++++++++########++++++++########++++++++
######++++++######++++++######++++++######++++++######++++++######++++++########++++++++########++++++++########++++++++
######++++++######++++++######++++++######++++++######++++++######++++++########++++++++########++++++++########++++++++
######++++++######++++++######++++++######++++++######++++++######++++++########++++++++########++++++++########++++++++
######++++++######++++++######++++++######++++++######++++++######++++++########++++++++########++++++++########++++++++
######++++++######++++++######++++++######++++++######++++++######++++++++++++++########++++++++########++++++++########
######++++++######++++++######++++++######++++++######++++++######++++++++++++++########++++++++########++++++++########
++++++######++++++######++++++######++++++######++++++######++++++######++++++++########++++++++########++++++++########
++++++######++++++######++++++######++++++######++++++######++++++######++++++++########++++++++########++++++++########
++++++######++++++######++++++######++++++######++++++######++++++######++++++++########++++++++########++++++++########
++++++######++++++######++++++######++++++######++++++######++++++######++++++++########++++++++########++++++++########
++++++######++++++######++++++######++++++######++++++######++++++######++++++++########++++++++########++++++++########
++++++######++++++######++++++######++++++######++++++######++++++######++++++++########++++++++########++++++++########
++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++
++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++
++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++
++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++
++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++
++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++
++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++
++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++
########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########
########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########
########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########
########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########
########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########
########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########
########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########
########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########
++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++
++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++
++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++
++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++
++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++
++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++
++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++
++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++
########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########
########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########
########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########
########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########
########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########
########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########
########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########
########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########
++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++
++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++
++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++
++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++
++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++
++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++
++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++
++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++
########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########
########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########
########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########
########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########
########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########
########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########
########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########
########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########++++++++########

Is an integer matrix permitted as output (e.g. 0s and 1s), or does it have to be strings/characters?

@Nick It has to be chars, sorry

Very well-written question!

@GregMartin Hey, thanks!

Jelly, 40 31 bytes
1SÆD>Ðḟ½ƊṀṭƲ³¡Äż$Ḷ:Ḃ^þ`ʋ/€ḷ""/Y

Try it online!
A full program taking the zero-indexed \$S-1\$ as input and writing to stdout ASCII art using 0 = #, 1 = +.
Without the trailing Y, this returns a list of lists of integers, but this is out of spec for this challenge.
Explanation
This program works in three stages.

Generate a list of values of \$k\$ and the cumulative sum of \$k\$
Generate a checkerboard for each of these with the tile size of \$k\$ and the board size of the cumulative sum
Work through the list of checkerboards, each time replacing the top-left section of the next board with the existing board.

Stage 1
1                 | Start with 1
           Ʋ³¡    | Loop through the following the number of times indicated by the first argument to the program; this generates a list of values of k
 S                | - Sum
        Ɗ         | - Following three links as a monad
  ÆD              |   - List of divisors
    >Ðḟ½          |   - Exclude those greater than the square root
         Ṁ        |   - Maximum
          ṭ       | - Concatenate this to the end of the current list of values of k
              Äż$ | Zip the cumulative sum of the values of k with the values

Stage 2
      ʋ/€ | For each pair of k and cumulative sum, call the following as a dyad with the cumulative sum of k as the left argument and k as the right (e.g. 15, 3)
Ḷ         | - Lowered range [0, 1 ... , 13, 14]
 :        | - Integer division by k [0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4]
  Ḃ       | - Mod 2 [0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0]
   ^þ`    | - Outer product using xor function and same argument to both side

Stage 3
   /  | Reduce using the following:
ḷ""   | - Replace the top left portion of the next matrix with the current one
    Y | Finally join by newlines

I think the asker actually wants the # and + characters. But wow, still impressive, I understood about half of it. And how do you even program in such a language? Is there a table of characters and their meaning where you just copy from?

@FabianRöling The OP states any two characters were acceptable. There’s a good introductory tutorial for Jelly at the https://github.com/DennisMitchell/jelly. If you know Python, the source is also fairly readable. Even with the tutorial and lists of atoms and Quicks, I found it took a little time and in some cases reference to the source to get my head round it.

Canvas, 34 32 bytes
０#０⁸［#+¶+#ｘｘ＊ｙｘ＋ｍ⤢αｍ；ｎｌｗ√｛ｙ；％‽²Ｘ

Try it here!

Input can now be 0-indexed; in case that helps

Python 2, 217 215 212 bytes
def f(x):
 b=['1'];n=1
 for i in range(x):P=max(j*(n%j<(j<=n**.5))for j in range(1,1+n));n+=P;b=[l+P*`j/P%2^i%2`for j,l in enumerate(b)];s=len(b[0]);b+=[((v*P+`1^int(v)`*P)*s)[:s]for v in b[0][len(b):]]
 return b

Try it online!
0-indexed, uses 0 and 1 as characters

@LuisMendo saved 2 bytes :D

Python 2, 184 178 176 169 bytes
def h(j,a=['1'],R=range):
 for i in R(j):L=len(a);k=max(x for x in R(1,L+1)if(x*x<=L)>L%x);a=[a[m]+k*`(i+m/k)%2`for m in R(L)]+[((`i%2`*k+`~i%2`*k)*L)[:L+k]]*k
 return a

Try it online!
Uses 1, 0 for #, -; uses 0-indexing.

JavaScript (ES7), 164 bytes
Input is 0-indexed. Outputs a matrix with \$0\$ for # and \$1\$ for +.
n=>(b=[1],g=(a,w,d=w**.5|0)=>b[n]?a:w%d?g(a,w,d-1):g(a.concat(Array(d).fill(b.push(d)&&i++)),w+d))([0],i=1).map((_,y,a)=>a.map((_,x)=>(x/b[v=a[x>y?x:y]]^y/b[v])&1))

Try it online!

Charcoal, 37 bytes
ＦＮ«≔⊕⌈Φ₂⊕Ｌυ¬﹪Ｌυ⊕κηＦη«ＰL⭆⊞Ｏυω§#+÷⁻κμη↙

Try it online! Link is to verbose version of code. 1-indexed. Output grows down and left (down and right costs an extra byte, but can grow up for the same byte count). Explanation:
ＦＮ«

Loop \$S\$ times.
≔⊕⌈Φ₂⊕Ｌυ¬﹪Ｌυ⊕κη

Calculate \$k\leq\sqrt{n+1}\$. This only makes a difference when \$n=0\$ in which case this formula allows \$k=1\$.
Ｆη«

Loop \$k\$ times, once for each new row and column.
ＰL⭆⊞Ｏυω§#+÷⁻κμη

Output the row and column, being sure to alternate between the # and + characters in such a way that # is always the first character but that there is a boundary at the end of the string (because we're drawing from the diagonal outwards). ⊞Ｏυω makes each row one character longer each time, which also keeps track of \$n\$ as the length.
↙

Move down and left ready for the next row.

05AB1E, 43 42 bytes
$G©ÐX‚ˆÑʒ®>t‹}àDU+}¯εÝ`θ÷É¨Dδ^}RζεðKζðδK€θ

Inspired by @NickKennedy's Jelly answer, and the trailing portion ζεðKζðδK€θ is a port from @Emigna's 05AB1E answer here.
Returns a matrix of 0 instead of # and 1 instead of +.
Try it online or try it online by outputting the first \$[2,n]\$ results (J, in the footer and the --no-lazy flag are to pretty-print the resulting matrix).
Explanation:
$                # Push 1 and the input
 G               # Loop the input - 1 amount of times:
  ©              #  Store the top of the stack in variable `r` (without popping)
   Ð             #  And triplicate the top as well
    X‚           #  Pair it with variable `X` (which is 1 by default)
      ˆ          #  And pop and store this pair in the global array
    Ñ            #  Get the divisors of the integer we triplicated
     ʒ    }à     #  Get the highest divisor which is truthy for:
         ‹       #   Where the divisor integer is smaller than
      ®>t        #   the square root of `r+1`
            DU   #  Store a copy of this largest filtered divisor as new variable `X`
              +  #  And add it to the triplicated integer
 }¯              # After the loop: push the global array
   ε             # Map each pair to:
    Ý θ          #  Convert the first value in the pair to a list in the range [0,n]
     `           #  and push both this list and the second value to the stack
       ÷         # Integer-divide each value in the list by the second value
        É        # Check for each value if it's even (1 if even; 0 if odd)
         ¨       # Remove the last item
          Dδ     # Loop double vectorized over this list:
            ^    #  And XOR the values with each other
   }R            # After the map: reverse the list of digit-matrices
     ζ           # Zip/transpose; swapping rows and columns, with a space as filler
      ε          # map each matrix to:
       ðK        #  Remove all spaces from the current matrix
         ζ       #  Zip/transpose with a space as filler again
          ðδK    #  Deep remove all spaces
             €θ  #  Then only leave the last values of each row
                 # (after which the resulting matrix of 0s and 1s is output implicitly)

Perl 6, 156 144 155 154 bytes
+11 to fix a bug reported by nimi.
{$!=-1;join "
",(1,{my \k=max grep $_%%*,1.. .sqrt;++$!;flat .kv.map(->\i,\l {l~($!+i/k)%2+|0 x k}),substr(($!%2 x k~1-$!%2 x k)x$_,0,$_+k)xx k}...*)[$_]}

Roughly based on Chas Brown's Python solution. Takes S zero-indexedly. Outputs 0 and 1.
Try it online!

Fixed. Now the corners should share the same colour.

Haskell, 149 146 bytes
(iterate g["#"]!!)
g b|let e=(<$[1..d]);l=length b;d=last[i|i<-[1..l],i*i<=l,mod l i<1];m="+#"++m=(e$take(l+d)$e=<<'#':m)++zipWith(++)(e=<<e<$>m)b

This is 0 indexed, returns a list of strings and grows upwards and leftwards.
Try it online!
(iterate g["#"]!!)                    -- start with ["#"], repeatedly add a layer
                                      -- (via function 'g'), collect all results in
                                      -- a list and index it with the input number

g b | let                             -- add a single layer to chessboard 'b'

 l=length b                           -- let 'l' be the size of 'b'
 d=last[i|i<-[1..l],i*i<=l,mod l i<1] -- let 'd' be the size of the new layer
 e=(<$[1..d])                         -- let 'e' be a functions that makes 'd'
                                      --   copies of it's argument
 m="#+"++m                            -- let 'm' be an infinite string of "+#+#+..."

 =                                    -- return
              zipWith(++)             --   concatenate pairwise
                         (e=<<e<$>m)  --   a list of squares made by expanding each
                                      --   char in 'm' to size 'd'-by-'d'
                                    b --   and 'b' (zipWith truncates the infinite
                                      --   list of squares to the length of 'b')
                                      --
           ++                         --   and prepend
                                      --
(e$take(l+d)$e=<<'#':m)               --   the top layer, i.e. a list of 'd' strings
                                      --   each with the pattern 'd' times '#'
                                      --   followed by 'd' times '+', etc., each
                                      --   shortened to the correct size of 'l'+'g'

all_licenses: Creative Commons - Attribution Share-Alike - https://creativecommons.org/licenses/by-sa/4.0/


text:
Expand a hexagon
Given an ASCII art hexagon as input, output one whose sides are all one unit longer.
                     _____
  ____              /     \
 /    \            /       \
/      \          /         \
\       \   =>    \          \
 \      /          \         /
  \____/            \       /
                     \_____/

The input hexagons will have 180 degree symmetry, but otherwise the sides can be all different lengths. Above, the side lengths (2, 4, 3) get enlarged to (3, 5, 4). The side lengths will be nonzero.
The hexagons are made of underscores _, slashes /, and backslashes \. Note that of the horizontal edges (made with underscores), the top edge is on its own line but the bottom edge is not.
I/O
I'm going to be a stickler and require ascii-art formatting here: the input and output should be a string with newlines representing the image, not a list of lines. Of course, your code may print each line in turn to produce the image, or read STDIN a line a time for input if your language can do that.
Details
The input may include an optional trailing newline if you wish, but will otherwise have no empty lines. You can choose to either have no trailing spaces in the input, or spaces to pad each line to the same length (that of the longest line).
The output should be flush with the left edge of the screen, like the input. You may have extra newlines above and below as well as trailing spaces.
Test cases
Input followed by output.
 _
/ \
\_/

  __
 /  \
/    \
\    /
 \__/

  ____
 /    \
/      \
\       \
 \      /
  \____/

   _____
  /     \
 /       \
/         \
\          \
 \         /
  \       /
   \_____/

     _
    / \
   /  /
  /  /
 /  /
/  /
\_/

      __
     /  \
    /    \
   /     /
  /     /
 /     /
/     /
\    /
 \__/

Leaderboard

<iframe src="https://xmikee1.github.io/ppcg-leaderboard/?id=185760" width="100%" height="100%" style="border: none;">Oops, your browser is too old to view this content! Please upgrade to a newer version of your browser that supports HTML5.</iframe><style>html,body{margin:0;padding:0;height:100%;overflow:hidden}</style>

Must the output hexagon be the same one but bigger, or can it be any hexagon with the appropriate side lengths?

@Stephen It must be the same one but bigger, in the same orientation.

This is a great example of a really well written challenge. Short, clear and to the point. May I make a small suggestion? "Stickler" may not be clear for all non native English speakers. May I suggest that "I will only accept..." or something similar may be easier to understand? +1 anyway.

Can we return an array/IEnumerable of characters?

@EmbodimentofIgnorance Yes, those are fine, as long as the have the required newline characters.

Stax, 28 bytes
╙Σ■♀♪«G[▀[TÖe╟╗ê'○▀ÄT→│╧(╡¢╩

Run and debug it
Seems like there should be a way to do it mostly with regex, but I'm still looking...

JavaScript (ES6),  159 156 153  150 bytes
s=>s[r='replace'](/\S /g,'$&   ')[r](/.*/,s=>s[r](e=/_+/,` $&_
`+s[r](e,'/$& \\')[r](e=/_/g,' ')))[r](/ *\\_+/,s=>s[r](e,' ')+`   /
 `+s[r](/_/,'__'))

Try it online!
Commented
s =>                       // s = input
  s[r = 'replace'](        // r = alias for 'replace'
                           // STEP #1
    /\S /g,                // insert two middle spaces for all lines
    '$&   '                // that contain a border, followed by a space
  )                        // (i.e. all lines except the first and the last one)
  [r](                     // STEP #2
    /.*/,                  // isolate the first line
    s =>                   // let s be this first line
      s[r](                //
        e = /_+/,          // find the sequence of underscores and replace it with:
        ` $&_\n` +         //   the same sequence preceded by a space and followed by '_'
        s[r](              //   followed by a linefeed and:
          e,               //     the same sequence preceded by '/' and followed by ' \'
          '/$& \\'         //
        )                  //     with:
        [r](e = /_/g, ' ') //     all underscores replaced with spaces
  ))                       //
  [r](                     // STEP #3
    / *\\_+/,              // isolate the last line, without the trailing '/'
    s =>                   // let s be this last line
      s[r](e, ' ') +       // replace all underscores with spaces
      `   /\n ` +          // append 3 spaces and a trailing '/', followed by a linefeed
      s[r](/_/, '__')      // append s with an extra underscore
  )                        //

Retina 0.8.2, 84 bytes
m`(¶.*)( .)$
$1   $2
(_+¶)(( */)  ( *.))
 _$1 $3$4¶$2
( *.)(_+/)$
$1$.2$*   /¶ $1_$2

Try it online! Works with irregular hexagons. I/O is unpadded. Explanation:
m`(¶.*)( .)$
$1   $2

Widen the interior of the hexagon.
(_+¶)(( */)  ( *.))
 _$1 $3$4¶$2

Fix up the top.
( *.)(_+/)$
$1$.2$*   /¶ $1_$2

Fix up the bottom.

Charcoal, 48 bytes
ＳθＷ¬№ω_≔⁺ωＳω≔⊕⊘№ω\η≔⊕⊘№ω/ζ≔×_⊕№ω_θ↗ζθ↓↘η←↙ζ↑←θ↖η

Try it online! Link is to verbose version of code. Explanation:
ＳθＷ¬№ω_≔⁺ωＳω

Input and concatenate all of the lines except the first. (Input in JSON format would make most of this unnecessary at a saving of 11 bytes.)
≔⊕⊘№ω\η≔⊕⊘№ω/ζ≔×_⊕№ω_θ

Count the number of /s, /s, and _s in the string and use that to calculate the new side lengths (in the case of _s, as a string of _s of that length).
↗ζθ↓↘η←↙ζ↑←θ↖η

Draw the enlarged hexagon.
Alternative solution, also 48 bytes:
ＳθＷ¬№ω_≔⁺ωＳω≔⁺θωθＦ²«≔Ｅ\/_⊕⊘№θκη×_⊟ηＭ⁰¬ιＦη«↷¹κ↷¹¶

Try it online! Link is to verbose version of code. Explanation:
ＳθＷ¬№ω_≔⁺ωＳω≔⁺θωθ

Input all of the lines. (Input in JSON format would make this unnecessary at a saving of 17 bytes.)
Ｆ²«

Draw the top right and bottom left sections of the hexagon separately.
≔Ｅ\/_⊕⊘№θκ

Count the number of /s, /s, and _s in the string and use that to calculate the new side lengths.
η×_⊟ηＭ⁰¬ι

Output the top or bottom, and move down a line if this was the top line.
Ｆη«↷¹κ↷¹¶

Draw both of the right or left sides.

APL (Dyalog Unicode), 75 74 bytesSBCS
' /\_'∘{⍺[a×1=(+⍀×a)⌊⊖+⍀⊖×a←2⌈/{⊃0~⍨1⌷⍵,⍨⍉⍵}⌺3 3(0,0,⍨⍉)⍣3⍉⍺⍳↑⍵]}'.+'⎕s'&'

Try it online!
'.+'⎕s'&' split input into lines
↑⍵ mix lines into a matrix
⍺⍳ replace ' /\_' with 0 1 2 3
(0,0,⍨⍉)⍣3⍉ surround with a layer of 0s on top&bottom and two layers of 0s on the left&right
{⊃0~⍨1⌷⍵,⍨⍉⍵}⌺3 3 for each cell choose the first non-0 from: upper, lower, left, right from the 3x3 neighbourhood centred on it
2⌈/ max in pairs horizontally
a×1=(+⍀×a)⌊⊖+⍀⊖×a← keep only the outer boundary of non-0s
⍺[ ] replace 0 1 2 3 with ' /\_'

PowerShell, 126 bytes
$args-replace' *\S +','$0   '-replace'( +)(_+)','$1 $2_
$1/$2 \'-replace'( *)(\\_+)/','$1$2   /
$1 $2_/'-replace'_(?=.* )',' '

Try it online!

Perl 5, 177 156 145 bytes
@l=pop=~/.+/g;splice@l,$_,0,$l[$_]=~s,_, ,gr for-1,1;$l[$_]=~s, (\S)( +)(\S),$_<2?" $1$2 $3":"$1$2   $3",e for 1..@l-2;join("\n",@l)=~s,_+,$&_,gr

Could be shorter? Don't see how just yet. With comments and added newlines and header+footer:
sub f {
@l=pop=~/.+/g;                                                      #1
splice@l,$_,0,$l[$_]=~s,_, ,gr for-1,1;                             #2
$l[$_]=~s, (\S)( +)(\S),$_<2?" $1$2 $3":"$1$2   $3",e for 1..@l-2;  #3
join("\n",@l)=~s,_+,$&_,gr                                          #4
}

Try it online!
Line #1 splits multi-line input string into the @l array.
Line #2 duplicates second and last lines without the _ chars.
Line #3 adds spaces where needed on line 2 to second last.
Line #4 widens the two ___ sides with one _ and returns the @l array of lines as one multi-line string.