Count how many times your program repeats

• The program outputs 1
• The program repeated n times outputs n
• The program backwards outputs -1
• The program backwards and repeated n times outputs -n
• A mix of forward and backward programs will output number_forward - number_backward

Take the program aB for example.

• aB should output 1
• aBaB should output 2
• Ba should output -1
• BaBa should output -2
• aBBa should output 0
• aBBaBa should output -1
• aaB, C, or ​ would not need to be handled

This is , so shortest in bytes per language wins.

• can you add test case aBBaaBBa ?
– 榨 菜
Dec 16, 2020 at 21:34
• I'm not sure if the rules as given would allow 0+1-0 as a Python solution, relying on the interpreter to implicitly print out the result of an expression formed by concatenating that or its reverse some number of times. Dec 17, 2020 at 0:30
• @DanielSchepler 0+1-0 is a great idea for an answer. You just need to pick the right language, e.g. tio.run/##y0osKPn/30DXUNvg/38A Dec 17, 2020 at 2:51
• @DigitalTrauma you should post that as an answer! Dec 17, 2020 at 9:31
• @DanielSchepler if you open a duplicate answer to this, I will happily delete this one. The credit should go to you. Dec 17, 2020 at 16:27

Jelly, 2 bytes

NC


Try it online! Or see all test-cases.

How?

In Jelly any sequence of monadic atoms forms a monadic chain which simply applies those monadic atoms in turn from left to right, starting with the value of it's left argument. When a full program is run with no arguments zeros are implicitly supplied as necessary, i.e. main_link(0) is called. As such NC is C(N(0)) and CN is N(C(0)).

NCNC ... CN ... NC - Main Link: no arguments
N                  - negate((implicit)0)     -1 *  0 =  0
C                 - complement(that)         1 -  0 =  1
N                - negate(that)            -1 *  1 = -1
C               - complement(that)         1 - -1 =  2
...           -
...           -                             ... =  a
C          - complement(a)            1 -  a =  1 - a
N         - negate(that)       -1 * (1 - a) =  a - 1
...    -
...    -                             ... =  b
N   - negate(b)               -1 *  b = -b
C  - complement(that)         1 - -b =  b + 1


Jelly, 3 bytes

I wonder if 2 bytes is possible in some language (I suspect that'd require it to consider the parity of the positions of the commands).

‘ḷ’


Explanation: ‘ increments, ’ decrements, and ḷ takes the left one, and for some magic reasons, when repeated, the program works as requested. Try it online!

• what in the world... Dec 16, 2020 at 15:13
• Do Jelly programs take 0 as a default when expecting a number but none is given? Curious how that works.
– xnor
Dec 16, 2020 at 15:14
• @xnor I have no idea how Jelly works :P I just remembered that an empty program outputs 0, and assumed that 0 is the default value for everything (which seems to be true) (and the syntax of using three functions in a row like this is the only thing I understood from the tutorial) Dec 16, 2020 at 15:17
• @xnor Yes, if a Jelly link is run niladically (without arguments), it assumes an argument of 0, except in a few specific circumstances Dec 16, 2020 at 16:30

Bash, 39

Thanks to:

• @pxeger for s/EXIT|TIXE/0/
• @Sisyphus for s/0 # 0/0/

trap echo\ $[++a] 0 ]a--[$ \ohce part



Try it online!

• maybe a newline would be necessary at the beginning for a backward followed by a forward Dec 16, 2020 at 18:10
• otherwise removing the # also works but writes on stderr, don't know if it's allowed Dec 16, 2020 at 18:16
• You can replace EXIT and TIXE with just 0 Dec 16, 2020 at 19:55
• @Jonah Yes, that's right. So for example after arithmetic expansion, 2 forwards would effectively be trap 'echo 1' 0; trap 'echo 2' 0. Dec 16, 2020 at 21:57
• If you don't mind writing a bunch of junk to stderr then you can replace 0 # 0 with just 0, and the extra args are ignored. Dec 16, 2020 at 23:04

Polyglot Japt, R, Applescript, 5 bytes

All credit for this one goes to @DanielSchepler. Daniel, if you open a duplicate answer to this, I will happily delete this one.

0+1-0


I don't know Japt at all, but this is just simple arithmetic evaluation. The result of the last evaluation is output at the end of the program by default. Japt was the first language that I found that satisfied this, but I'm sure there are others.

Forward (1): Try it online!

Forward-Backward-Backward-Forward-Forward-Backward-Backward (1):Try it online!

• This is a brilliant answer! You (or Daniel) could label it a polyglot
– user
Dec 17, 2020 at 16:37
• Also works in R. Dec 17, 2020 at 20:30
• Also works in Javascript. Mar 23, 2021 at 8:41
• Also works in Pip. Jun 30, 2021 at 0:18

Perl 5-0777p, 13 bytes


++$_;#;_$--



Try it online!

05AB1E, 4 bytes

¾¼.¾


Explanation:

The 1x forward program does the following:

¾     # Push the counter variable to the stack (0 by default)
¼    # Increment the counter variable
.   # No-op, because .¾ doesn't exist as 2-byte command, so it just ignores this
¾  # Push the counter variable to the stack (which is 1 now)
# (implicitly output the top of the stack as result)


The 1x backward program does the following:

¾     # Push the counter variable to the stack (0 by default)
.¼   # Decrement the counter variable
¾  # Push the counter variable to the stack (which is -1 now)
# (implicitly output the top of the stack as result)


Any combination of these two programs after one another increments and decrements the counter variable, but only the last value pushed to the stack is output implicitly at the very end.

R, 26 bytes

F+
1->F#F=FF
FF=F#F>-1-
+F


Try it online!

!enilno ti yrT

Twice backwards and four times forwards

A very different (and shorter) approach to Giuseppe's R answer (in which he predicted that this answer was coming!).

F is initially a shorthand for FALSE and so gets coerced to 0. When the program is run forwards, we use rightwards assignment to get F=F+1 and FF=F. When concatenated with itself, the program becomes

F+
1->F#F=FF
FF=F#F>-1-
+FF+
1->F#F=FF
FF=F#F>-1-
+F


Try it online!

and so the second time, we are doing F=+FF+1 which also increments F.

When run backwards, the only difference is that we use +-1 instead of +1.

When the final line is run, we print +F.

• Well there ya go! Automatic line continuation is useful here too. I'll come back and bounty this once the question is eligible. Dec 17, 2020 at 15:14
• Great! I tried for ages to get something like this to work, without success... Well done! Dec 17, 2020 at 19:52

C++ (gcc), 324325 323 bytes

Note the leading and trailing newlines.

Thanks to Jonathan Allen for spotting the need for a leading newline.


#ifndef M//
#define M(a,b)a##b//
#define W(z,x)M(z,x)//
#import<cstdio>//
int n;struct X{X(int d){n+=d;}};main(){printf("%d",n);}//
#endif//
X W(a,__LINE__)=1;//;1-=)__ENIL__,s(W X
//fidne#
//};)n,"d%"(ftnirp{)(niam;}};d=+n{)d tni(X{X tcurts;n tni
//>oidtsc<tropmi#
//)x,z(M)x,z(W enifed#
//b##a)b,a(M enifed#
//M fednfi#



Try it online!

aBaB (2)

Ba (-1)

BaBa (-2)

aBBa (0)

BaaB (0)

aBBaBa (-1)

Rundown

Comments are used to make the code legal in forward as well as reversed form.

#ifndef M//
...
#endif//


These are your basic guards to make sure we only define things once.

int n;struct X{X(int d){n+=d;}};main(){printf("%d",n);}//


Inside the guard we also declare the integer n, which will be automatically initialized to 0 by virtue of being a global, as well as define a struct X, whose constructor takes an integer value to add to the global n. The main() function simply prints the value of n.

main() is run after all global variables have been sorted out, so we use the declaring of globals as a way to alter that value of n before main() is entered.

This requires a way to declare an arbitrary amount of variables, leading to:

#define M(a,b)a##b//
#define W(z,x)M(z,x)//


The main magic of the stew: Helper macroes that allow the gluing of tokens together in a very useful way. Calling W(a,b) will yield the token ab, with the potent twist of evaluating b as a macro first, which makes these kinds of things possible:

#define M(a,b)a##b
#define W(z,x)M(z,x)
#define b 10
int W(a,b);          // This makes an int called "a10"


We combine this with the special preprocessor macro __LINE__ which always expands to the current line number, allowing us a way to produce an arbitrary amount of variables having unique names.

X W(a,__LINE__)=1;//;1-=)__ENIL__,s(W X


Suppose we are at line 553; this will then declare a variable a553 of type X, running its constructor with the argument 1. The reversed version is identical, save for using -1 instead.

Our preprocessor guards make sure the above line and its reverse are the only things repeated.

• I think it needs a leading newline too. Dec 17, 2020 at 0:56
• @JonathanAllan Ah, yes, indeed! Dec 17, 2020 at 1:01

Nim, 115 bytes


when not declared i:#1=-i
var i=0;addQuitProc do:echo i#i ohce:od corPtiuQdda;0=i rav
i+=1#:i deralced ton nehw



There is a leading and trailing newline. The second line has a trailing space.

Try it online!

Try it online!Try it online!Try it online!

!enilno ti yrT

!enilno ti yrT!enilno ti yrTTry it online!!enilno ti yrT

Ruby, 41 bytes


$.+=1;END{exit!p$.}#}.$p!tixe{DNE;1=-.$



Try it online!

R, 99 91 bytes


l=readLines();*=grepl;1+sum("^l"*l-"^r"*l)#1-)r*'r^'-r*'l^'(mus;lperg=*;)(seniLdaer=r



Try it online!

-8 bytes thanks to Dominic van Essen!

Tripled

Reversed

Forwards and Backwards

R has an odd but often useful (and not just for golfing) feature where data can be stored in the code, and read in as such. readLines() reads in lines until the end of the file. Then the grepl counts lines beginning with l (adding 1) and the ones beginning with r (adding -1), then incrementing/decrementing as appropriate: the first non-empty line is not counted as it's the only code that is run.

Starts/ends with a newline for reversing and concatenation purposes.

Possibly some -> or <- chicanery will yield a shorter result -- indeed, Robin Ryder has proven me right, and it's shorter by a lot!

• Very cool! I think you could save 2 bytes by using grepl("l<",l<-readLines()) and the equivalent for r. (And I've been working on the chicanery, but haven't got it to work yet!) Dec 16, 2020 at 23:16
• Yes! Really nice! I haven't figured-out how to get Robin's idea with the l< regex to avoid matching the regex itself, but this seems to be Ok for 90 bytes... Dec 17, 2020 at 0:04
• @DominicvanEssen Ah, very nice. I think it's 91 since there needs a leading newline for a program that starts with a "reverse" and has some more concatenations. Dec 17, 2020 at 1:15
• 26 bytes of chicanery. ;-) Dec 17, 2020 at 8:41

Brain-Flak (Rain-Flak), 16 bytes

({}())#)])([}{(



Try it online!

Explanation

Making a program that when repeated counts up is easy

({}())


This adds 1 and with no input it defaults to zero, so repeating it counts the number of times.

However reversing is a little trickier. Only one balanced string is also a balanced string in reverse and that is the empty string, so we need to use some comments. We use the line comments of the Rain-Flak interpter and hide a payload between a # and a newline. Which simply is

({}[()])


This works the same way as the original program but with minus 1 instead of 1.

Python IDLE, 5 bytes

0-~+0


Try it online!

Explanation

Forwards

0      # zero
-~    # minus-complement (the increment prefix "operator", which
#                   also works as a binary add-plus-one)
+0  # zero


Concatenated

0           # zero
-~         # plus one plus
+00      # zero
-~    # plus one plus
+0  # zero


Reversed

0      # zero
+     # plus
~-   # complement-minus (the decrement prefix "operator")
0  # zero


Bonus variants

• 0-~-0, which counts up regardless of flipping (in fact, it's palindromic);
• 0+~+0, which counts down regardless of flipping.

Vyxal, 5 bytes


›#‹



Try it Online!

Try it Online!Try it Online!

Try it Online!Try it Online!Try it Online!Try it Online!Try it Online!

!enilnO ti yrT

!enilnO ti yrTTry it Online!!enilnO ti yrT

main=print$0--1- +1--0$tnirp=niam


Try it online!

I tried for quite a bit to do this without comments. It doesn't seem impossible, but it does seem very hard.

Wolfram Language (Mathematica REPL), 5 bytes

0+1-0


Test cases:

0+1-0            1
0+1-00+1-0       2
0-1+0           -1
0-1+00-1+0      -2
0+1-00-1+0       0
0+1-00-1+00-1+0 -1


Try it online!

• This is a snippet, since Mathematica doesn't implicitly output (see comments on the question)
– att
Dec 17, 2020 at 7:15
• If you want to keep your answer like this you can switch to a language like bc that implicitly outputs the result.
– ovs
Dec 17, 2020 at 10:14
• See the When a REPL acceptable meta and similar. I shall edit to add that this is explicitly REPL, or is run from the command line, both which implicitly echo the final value. Mathematica "script" is not used as often from what I can tell, but is what people are expecting when you say Mathematica. Dec 17, 2020 at 16:07

ARM Thumb, 8 bytes excluding return statement

Raw machine code:

2101 1840 3901 2100


Assembly:

        .thumb
@ R1 = 1
movs    r1, #1
@ r0 += 1
@ Dummy code
subs    r1, #1
movs    r1, #0


Reverse each 16 bits

2100 3901 1840 2101

        .thumb
@ r1 = 0
movs    r1, #0
@ r1 = -1
subs    r1, #1
@ r0 += -1
@ Dummy code
movs    r1, #1


Going forwards, it sets r1 to 1 and adds it to r0

Going backwards, it sets r1 to 0, subtracts 1 to get r1 to -1 (since Thumb can only load 0-255), then adds it to r0 effectively subtracting 1.

Call it with r0 = 0.

You need to end it with a return statement (bx lr or 4770 in hex) at the end, but those four instructions can be repeated forward and backwards as many times as you desire.

ARM (NOT thumb), 16 bytes excluding return instruction

Second, entirely different answer, still 4 instructions though.

Machine code:

01 10 80 e3 01 00 80 e0 e0 80 00 01 e3 f0 10 00


This one, unlike my Thumb submission, is entirely different in that it is byte reversible (hence why it is split in 8 bits instead of 32 bits)

It happens to do basically the same thing, though, add 1 on forwards and -1 on backwards.

Forwards:

        movs      r1, #1
smlatteq  r0, r0, r0, r8
andseq    pc, r0, r3, ror #1


Backwards:

        movs      r1, #-1   @ a.k.a. mvns      r1, #0
smlatteq  r0, r0, r0, r8
tsteq     r0, r3, ror #1


You may be asking yourself, "smlatteq? andseq? tsteq? What the **** do those do?"

The answer is nothing unless the zero flag is set.

This code heavily abuses ARM's conditional execution and "s" instructions.

movs will set the condition flags on the value it sets (why this exists for the immediate form, I will never know). I set them to 1 on forwards, and -1 on backwards. These are both non-zero, so they clear the zero flag.

We also use the version of add which does not update the flags, so the zero flag remains clear.

Therefore, since the zero flag is not set, all instructions with the eq suffix will be ignored.

And that is a VERY good thing, because ands pc, r0, r3, ror #1 would almost certainly crash the program (it would jump to a garbage address)

Therefore, in terms of what is effectively executed, it is this:

Forwards:

        movs      r1, #1
nop
nop


Backwards:

        movs      r1, #-1           @ a.k.a. mvns      r1, #0
nop
nop


Similar to my Thumb answer, you can repeat these as much as you wish, but you must put a bx lr (e12fff1e) at the end, and it is called with r0 == 0.

Python 3, 109 107 bytes

Same approach as my zsh answer, but it actually beats the other Python answer!

c=open(__file__).read().count;print(c('w	')-c('y\t'))#))'	y'(c-)'t\w'(c(tnirp;tnuoc.)(daer.)__elif__(nepo=c


Try it online!

-2 bytes thanks to @Jakque

• Reading the program's own code isn't usually allowed, is it? Or is that rule only for quines? Dec 17, 2020 at 18:38
• @user253751 as far as I can see, that rule only applies for quines yes. Dec 17, 2020 at 20:19
• You can save 1 byte by replacing 'y''z' by 'y\t' and 'zy' by '<tab>y' (You can do it for forward and backward so 1+1 = 2 bytes saved): Try it online! Jul 2, 2021 at 8:22

Python 3.8 (pre-release), 77 71 bytes

for i in(1,a:=0):#)a(tnirp ro i=-a
a+=i or print(a)#:)0=:a,1(ni i rof


Try it online! (include testcases)

How it works

The starting code is this :

for i in(1,a:=0):#<...>
a+=i or print(a)#<...>


At the first iteration of the for loop

• The for loop assigns a to 0 and i to 1.
• since i is equal to 1, i or print(a) won't evaluate the print and will be equal to 1
• a+= we increment a (become a-= in the reversed program)

At the second iteration of the for loop:

• The for loop assigns i to 0.
• i or print(a) will print a since i is evaluated as False and return None
• it then crash the program (to avoid multiple printing) since we try to add None to an integer

When chained, the prgrams become :

for i in(1,a:=0):#<...>
a+=i or print(a)#<...>for i in(1,a:=0):#<...>
a+=i or print(a)#<...>


There is only one active for loop (the other one are commentated), and the incrementation (or decrementation when reversed) occurs once for each repetition of the program. The printing occurs only once beacause of the crash.

The reverse program is only changing a+= by a-=

Charcoal, 16 bytes

υΣＩ⁰⎚¹±υ⊞υ¹⎚⁰ＩΣυ


Try it online! Explanation:

υ


Print the list.

ΣＩ⁰


Cast 0 to string, then take the digital sum and print it (this happens to do nothing).

⎚


Clear the canvas.

¹


Print a -.

±υ


Print the negated list.

⊞υ¹


Push 1 to the list. (This is what actually affects the final output.)

⎚


Clear the canvas.

⁰


Print nothing.

ＩΣυ


Cast the sum of the list to string and print it. (This code at the end of the last copy is the only output that survives the program.)

Try it reversed! Explanation: The middle instructions differ slightly when the code is reversed:

¹


Print a -.

υ


Print the list.

⊞υ±¹


Push -1 to the list.

• This looks really impressive Dec 16, 2020 at 23:48

Python 3, 164 bytes


import atexit as a;a._clear();a.register(lambda:print(v));v=locals().get('v',0)+1#;1-)0,'v'(teg.)(slacol=v;))v(tnirp:adbmal(retsiger.a;)(raelc_.a;a sa tixeta tropmi



Try it online! Or use this program builder (Input F for forward and R for reverse).

• You can use vars instead of locals Dec 17, 2020 at 8:37

Zsh, 91 bytes

<<<$[grep -c wx$0-grep -c y\z $0];:<<'Q' 'Q'<<:;]0$ zy c- perg-0$x\w c- perg[$<<<


Try it online!

Explanation

The overall idea is to read the source file and count the number of occurrences of the code forwards, minus the number backwards.

     grep -c wx $0 # count occurrences of wx grep -c y\z$0          # count occurrences of yz*
$[ - ] # subtract <<< # print ; # then : # do nothing, <<'Q' # with the rest of the file**  *: The backslash in the middle prevents the call to grep itself from being counted as an instance. In the mirrored copy on the next line, the backslash is placed inside wx instead, so that when re-reversed, it matches the negative one instead. **: This starts a here-document, which consumes as input the rest of the file until the first line containing only FW. The single-quotes prevent execution of any $[...] and ... parts from any possible repeated sections afterwards. We can't use line comments because grep -c outputs the number of matching lines, not individual matches. In another language we won't have this limitation.

The whole program is then duplicated, with the \ inside the wx instead of the yz, is then reversed and put on the next line, so that it works as expected when the first instance is a reversed one.

Klein 000, 12 bytes

\@+
-
1
+@\


Try it online!

Reversed

\@+
1
-
+@\


Try it online!

Wolfram Language (Mathematica), 31 bytes

tnirP//1-eniL$--$Line++//Print


Try it:

A full program. \$Line has an initial value of 1. In a notebook, it would increment for each input expression, but this doesn't apply outside the REPL.

JavaScript, 131121 122 bytes

• -10 thanks to Arnauld
• +1 I forgot that the newline would also be reversed, so another one needed to be added

clearTimeout(this.a);a=setTimeout(console.log,0,b=-~this.b)//)b.siht-~=b,0,gol.elosnoc(tuoemiTtes=a;)a.siht(tuoemiTraelc;



(notice the leading and trailing newlines. the stacksnippets removes some of the leading and trailing newlines for the entire snippet, but it still works with/without the first and last newlines)

Try It Right Here! (TryItOnline has an error where clearTimeout is not defined, but clearTimeout is a valid JavaScript function.)

aB

clearTimeout(this.a);a=setTimeout(console.log,0,b=-~this.b)//)b.siht-~=b,0,gol.elosnoc(tuoemiTtes=a;)a.siht(tuoemiTraelc

aBaB

clearTimeout(this.a);a=setTimeout(console.log,0,b=-~this.b)//)b.siht-~=b,0,gol.elosnoc(tuoemiTtes=a;)a.siht(tuoemiTraelc

clearTimeout(this.a);a=setTimeout(console.log,0,b=-~this.b)//)b.siht-~=b,0,gol.elosnoc(tuoemiTtes=a;)a.siht(tuoemiTraelc

Ba

clearTimeout(this.a);a=setTimeout(console.log,0,b=~-this.b)//)b.siht~-=b,0,gol.elosnoc(tuoemiTtes=a;)a.siht(tuoemiTraelc

BaBa

clearTimeout(this.a);a=setTimeout(console.log,0,b=~-this.b)//)b.siht~-=b,0,gol.elosnoc(tuoemiTtes=a;)a.siht(tuoemiTraelc

clearTimeout(this.a);a=setTimeout(console.log,0,b=~-this.b)//)b.siht~-=b,0,gol.elosnoc(tuoemiTtes=a;)a.siht(tuoemiTraelc

aBBa

clearTimeout(this.a);a=setTimeout(console.log,0,b=-~this.b)//)b.siht-~=b,0,gol.elosnoc(tuoemiTtes=a;)a.siht(tuoemiTraelc

clearTimeout(this.a);a=setTimeout(console.log,0,b=~-this.b)//)b.siht~-=b,0,gol.elosnoc(tuoemiTtes=a;)a.siht(tuoemiTraelc

aBBaBa

clearTimeout(this.a);a=setTimeout(console.log,0,b=-~this.b)//)b.siht-~=b,0,gol.elosnoc(tuoemiTtes=a;)a.siht(tuoemiTraelc

clearTimeout(this.a);a=setTimeout(console.log,0,b=~-this.b)//)b.siht~-=b,0,gol.elosnoc(tuoemiTtes=a;)a.siht(tuoemiTraelc

clearTimeout(this.a);a=setTimeout(console.log,0,b=~-this.b)//)b.siht~-=b,0,gol.elosnoc(tuoemiTtes=a;)a.siht(tuoemiTraelc

• That's a really ingenious use of the setTimeout and clearTimeout functions! Dec 17, 2020 at 7:58
• You can save some bytes by using -~this.b and ~-this.b instead of (this.b||0)+1 and (this.b||0)-1 respectively. Dec 17, 2020 at 8:11
• Thanks @Arnauld, I always forget that ~undefined, ~NaN, ~null, etc. (anything that is relatively a 0) results in -1. Dec 17, 2020 at 8:23
• Also, since this is intended to be run in a browser, you could just use alert instead of console.log. (But because the popups are not very user-friendly, you may want to add alert = console.log in the code snippets.) Dec 17, 2020 at 8:41

Python 3, 112 bytes


import os#1=-KO_R.so
os.system('cls')#)5-KO_R.so(tnirp
print(os.R_OK-3)#)'slc'(metsys.so
os.R_OK+=1#so tropmi



very ugly, but works.

• Welcome to Code Golf! This is a nice first answer. I don't know how your program is supposed to work, so this link doesn't work currently, but I'd recommend editing that and adding it to your answer. TIO also automatically counts bytes and generates Markdown for your answer, which is nice.
– user
Dec 18, 2020 at 23:34

JavaScript (Node.js), 104 bytes


var i=-~i,y=-~y;setTimeout(_=>--i||console.log(y))//))y(gol.elosnoc||i-->=_(tuoemiTtes;y-~=y,i~-=i rav



Try it online!

Using V8(print) or browser(alert) saves some bytes

Ruby, 60 bytes

Note trailing newline:

a||=0;a+=1;at_exit{p a;exit!}#}!tixe;a p{tixe_ta;1=-a;0=||a



Try it online!


a||=0;a-=1;at_exit{p a;exit!}#}!tixe;a p{tixe_ta;1=+a;0=||a


Try it reversed!

at_exit specifies a block to be executed upon exit, SystemExit exception, or uncatchable error. exit! specifically bypasses these, giving the ability to skip all but the first registered block.

><>, 19 bytes

Forwards

l\ v
;l-n
n+l;
v \l


Try it online!

Backwards

l\ v
;l+n
n-l;
v \l


Try it online!

Works a lot like my klein solution, but it has to deal with the finickiness of ><>.