| Bytes | Lang | Time | Link |
|---|---|---|---|
| 065 | sed 4.2.2 E | 240724T163625Z | Jordan |
| 134 | C gcc | 240821T034607Z | Conor O& |
| 153 | tinylisp 2 | 240819T194750Z | DLosc |
| 032 | Uiua | 240819T193907Z | janMakos |
| 131 | Haskell | 240729T214810Z | DPD- |
| 096 | StackControl 1.1 | 240805T191746Z | Швеев Ал |
| 102 | jq | 240805T114251Z | GammaFun |
| 086 | Python | 240724T043605Z | Mukundan |
| 208 | GNU C | 240725T221756Z | Knogger |
| 092 | Zsh | 240724T174928Z | GammaFun |
| 040 | Uiua | 240724T170308Z | janMakos |
| 120 | Google Sheets | 240724T111238Z | z.. |
| 092 | Python | 240724T062044Z | Albert.L |
| 034 | 05AB1E | 240723T130521Z | Kevin Cr |
| 193 | Setanta | 240723T223355Z | bb94 |
| 050 | Charcoal | 240723T215243Z | Neil |
| 080 | JavaScript Node.js | 240723T100159Z | l4m2 |
| 071 | Perl 5 p | 240723T093221Z | ovs |
| 053 | Retina 0.8.2 | 240723T094301Z | Neil |
| 137 | Python 3.8 prerelease | 240723T091424Z | Jitse |
sed 4.2.2 -E, 65 bytes
The input string and sequence of moves should be separated by a tab. Code contains literal tab characters (which SE has replaced with spaces below, alas).
:
s/(.)?\|(.* )</|\1\2/
s/\|(.)?(.* )>/\1|\2/
s/.?\|(.* )#/|\1/
t
C (gcc), 134 bytes
j;f(c,i,q)char*c,*i,*q;{for(q=strchr(c,'|');*i;i++)*i%2?q-c?strcpy(q-1,q),q--:7:*(q-=j=q-c|*i%3?1-*i%3:0)?j?*q^=q[j]^=*q^=q[j]:0:q--;}
Try it online! Utilizes UB, but if it works, it works™. Reusable function which takes two char* inputs, and outputs by modifying its first parameter. All char* are NULL-terminated. Uses K&R syntax to declare the function, which also declares (but does not accept for the purposes of this challenge) a third char* parameter.
Extra test cases generated using this script, which (arbitrarily) uses Jitse's Python answer as a ground truth.
Experience
I find golfing conditional code somewhat challenging, so I wanted to share some progress for the main conditional deciding whether or not we should move the cursor. I initially had:
j=1-*i%3;q==c&&j>0?j=0:1; // 25 bytes
j=*i%3;j=q==c&&!j?0:1-j; // 24 bytes (flips j's sign, refactored following code)
j=*i%3;j=q-c||j?1-j:0; // 22 bytes
j=*i%3;j=q-c|j?1-j:0; // 21 bytes (we don't need short circuiting)
j=q-c|*i%3?1-*i%3:0; // 20 bytes (cheaper to reuse *i%3)
This last line also allows us to inline *(q-=j) that we had to use when we had multiple statements to *(q-=j=...).
Readable and commented version
j; // temp integer used to represent cursor movement
f(c, i, q)
char *c, // the text to operate on
*i, // cursor instructions
*q; // dummy temp buffer
{
// q is a pointer to our cursor
for(q = strchr(c, '|'); *i; i++) {
// decide between '#' and '<>'
// '<' = 60 '>' = 62 '#' = 35
// 60 % 2: 0 62 % 2: 0 35 % 2: 1
if(*i % 2) {
// delete
if(q - c) {
// only delete if our cursor is not at the start
strcpy(q - 1, q); //shift characters after cursor left
q--; // update the cursor's position to reflect shift
}
}
else {
// move
// '<' = 60 '>' = 62
// 60 % 3: 0 62 % 3: 2
if(q - c | *i % 3) {
// if we are at the start (q - c), only execute '>' moves
// if we are not at the start, execute any cursor moves*
j = 1 - *i % 3;
}
else {
// otherwise, the cursor will not move
j = 0;
}
// move the cursor pointer accordingly
q -= j;
if(*q) {
// *we check to see if the cursor result points at a null byte
if(j) {
// only swap if we actually moved, since the XOR trick
// zeroes *q if *q and q[j] are the same address
// if *q is not null, we execute the cursor movement
// XOR swap trick thanks to Karl Napf
// https://codegolf.stackexchange.com/a/111495/31957
*q ^= q[j] ^= *q ^= q[j];
// even though j is -1 or 1, we can use q[j] since
// q[j] <=> *(q + j)
// thus, q[-1] is the char to the left of the cursor
}
}
else {
// *q is null, we've moved too far right, and must undo,
// lest our null-terminated string be ruined
q--;
}
}
}
}
tinylisp 2, 153 bytes
(d G(\(A M B)(: R(=(h M)62)(? M(G(? R(,(] 1 B)A)(t A))(t M)(? R(t B)(,(](=(h M)60)A)B)))(,(~ A)(,"|"B
(\(S M)(: I(first-index S 124)(G(~(] I S))M(t([ I S
The first line defines a helper function; the second line is an anonymous function that takes the starting string and the string of moves and returns the result string. Try It Online!
Ungolfed/explanation
The main function splits the string into two halves at the | (ASCII 124) and passes them with the move-string to the helper function, reversing the left half so the business end is in front:
(def apply-moves
(lambda (string moves)
(let pipe-index (first-index string 124)
(_apply-moves
(reverse (take pipe-index string))
(tail (drop pipe-index string))
moves))))
The helper function recurses while there are moves remaining:
- On a
>(ASCII 62), concatenate the first character from the right half (if any) to the left half; otherwise, take the tail of the left half. - On a
>, take the tail of the right half; otherwise, on a<(ASCII 60), concatenate the first character from the left half (if any) to the right half; otherwise, leave the right half unchanged. - Take the tail of the move-string.
When the move-string is empty, reverse the left half and concatenate it to | and the right half.
(def _apply-moves
(lambda (left right moves)
(if moves
(_apply-moves
(if (= (head moves) 62)
(concat (take 1 right) left)
(tail left))
(if (= (head moves) 62)
(tail right)
(if (= (head moves) 60)
(concat (take 1 left) right)
right))
(tail moves))
(concat
(reverse left)
(concat "|" right)))))
Uiua, 32 bytes
∧(⍜⊙⊜□⍚⨬⋅@|⇌±⊙⊸⦷⟜⍥⇌⊙"\W|")⊗⊙"#>"
I am not editing my previous answer because this solution is radically different.
∧(⍜⊙⊜□⍚⨬⋅@|⇌±⊙⊸⦷⟜⍥⇌⊙"\W|")⊗⊙"#>"
⊗⊙"#>" # enumerate the instructions
∧( ) # for each instruction:
⊙"\W|" # make a pattern string with `|`
# and wildcard (like `*` in regex)
⟜⍥⇌ # reverse the pattern if instruction is `>`
⊙⊸⦷ # find all matches of the pattern
⍜⊙⊜□⍚ # operate on each match
⨬ ± # if instruction is `#`
⋅@| # delete preceding character
⇌ # else move the cursor
Haskell, 133 131 bytes
- -2 bytes thanks to @DLosc
f=(intercalate"|".).foldl h.splitOn"|"
h[x,y:z]'>'=[x++[y],z]
h x@[[],_]_=x
h[x,y]'<'=h[x,last x:y]'#'
h[x,y]'#'=[init x,y]
h x _=x
- The string is split at | into two strings
- For each move the last character of the first string and / or the first of the second are modified (helper function h)
- The two strings are joined with a | in the mid
StackControl 1.1, 96 characters
So basicly i unpack string on to stack and move on it directly, a lot of space get taked to prevent cursor go of the edge of the stack
[0 0R 0 0⟧←←←:"|"⊗⍃⇆("|"≠)⊚⍄R⇆(→⇆)⟲(:"<"=(,:#→¿←)(:">"=(,→:#←¿)(:"#"=(,:#,?)?)⁇)⁇)∴"|"]→⟦⇆⊍⌦⌦⌫⌫W
jq, 102 bytes
reduce.m[]as$m(.s;{">":sub("\\|(?<c>.)";.c+"|"),"<":sub("(?<c>.)\\|";"|"+.c),"#":sub(".\\|";"|")}[$m])
Input as {"s":"the| current buffer","m":[">","<","#","#"]}. The TIO link splits the motions in the header for testing convenience.
On each motion, compute each possible change as values in a JSON object, then select the correct result.
reduce .m[] as $m ( # Repeat for each motion, where the motion is bound to $m
.s; # Initial input is the string
{ # Replace current input with...
">": sub("\\|(?<c>.)";.c+"|"),
"<": sub("(?<c>.)\\|";"|"+.c),
"#": sub(".\\|";"|")
}[$m] # ...value at key $m
)
If the motions need to be passed as a single string, then +5 bytes by using reduce(.m/"")[]as $m instead.
Python, 100 96 87 86 bytes
-4 bytes thanks to @xnor
-9 bytes thanks to @no comment
def f(s,o):
for i in o:s[(j+i%5or 1)-1:0]=s.pop(j:=s.index('|'))[i%2>0<j==s.pop(j-1)]
Modifies the input in-place
GNU C, 214 208 bytes
- -6 bytes by replacing all
*(x+y)byx[y]
char*c(char*s,char*i){char*r=malloc(strlen(s)),*z,o,t;r=strcpy(r,s);for(;*i;++i){z=strchr(r,'|');o=(*i==60&&z-r)?-1:(*i==62&&z[1])?1:0;t=*z;*z=z[o];z[o]=t;if(*i==35&&z-r)memmove(z-1,z,strlen(z)+1);}return r;}
Zsh, 103 92 bytes
Saved 11 bytes using r[1]= to remove the first character of $r and l[-1]= to remove the last character of $l.
IFS=\|
read l r
for m;case $m {\<)r=$l[-1]$r l[-1]=;;\>)l+=$r[1] r[1]=;;*)l[-1]=;}
<<<$l\|$r
Takes string on stdin and motions as argv.
IFS=\| # set internal field separator for reading string
read l r # read string from stdin, splitting on $IFS into $l and $r
for m; # motions as separate arguments
case $m {
\<)
r=$l[-1]$r l[-1]= ;;
\>)
l+=$r[1] r[1]= ;;
*) # catchall, rather than escaping \#
l[-1]=
}
<<<$l\|$r
Uiua, 40 bytes
⍜⊙↻⊂@|∧⊃(+-⊸¬=@>|⍜⊙↻↘=@#⊙-⊙1):⊙:⊙▽⊃⊗⊸≠@|
Explanation
⊙▽⊃⊗⊸≠@| # Get the (0 indexed) position of the cursor `|`
# and remove it from the string
:⊙: # Rearrange items before looping
∧⊃( # For each character in the moves string do:
+-⊸¬=@> # Add 1 to the cursor position if the move is `>`
# otherwise subtract 1
| ⍜⊙↻↘=@#⊙-⊙1 # If the move is `#` backspace remove the character
# 1 index before the cursor position
) # End loop
⍜⊙↻⊂@| # Insert the cursor back into the string at cursor position
Google Sheets, 120 bytes
Expects the string in A2 and the moves in B2:
=REDUCE(A2,SEQUENCE(LEN(B2)),LAMBDA(a,i,REGEXREPLACE(a,"(.?)\|(.?)",SWITCH(MID(B2,i,1),">","$1$2|","<","|$1$2","|$2"))))
Python, 92 bytes
-1 thanks to @xnor
lambda s,c:[s:=re.sub((2*"((\.|))")[i<"="::2],r"\2\1"[:ord(i)&6],s)for i in c][-1]
import re
Python, 93 bytes
lambda s,c:[s:=re.sub((2*"((\.|))")[i<"="::2],r"\2"+r"\1"*(i>"#"),s)for i in c][-1]
import re
Inspired by other regex based answers.
05AB1E, 35 34 bytes
Çvā<VÐ'|kDy61.S+‚YÃUy₆›iXèë'|æ}RXǝ
Inputs in the order \$moves,string\$.
Try it online or verify all test cases or see all intermediate steps by adding a trailing =.
Explanation:
Ç # Convert the first (implicit) input-moves to a list of codepoint-integers
v # Pop and loop over each codepoint `y`:
ā # Push a list in the range [1,length] (without popping)
# (using the implicit second input-string in the first iteration)
< # Decrease it to 0-based range [0,length)
V # Pop and store it in variable `Y`
Ð # Triplicate the current string
'|k '# Pop one, and get the (0-based) index of "|"
D # Duplicate this index
y61.S # Compare the current codepoint `y` with 61
# (1 if >61 and -1 if <61: ">" is 1 and "<"/"#" are -1)
+ # Add that to the duplicated index
‚ # Pair the two together
YÃ # Only keep indices that are in range based on `Y`
U # Pop and store this pair (or singleton) in variable `X`
y₆›i # If `y` is an arrow (codepoint > 36):
Xè # Get the character(s) of the string at index/indices `X`
ë # Else:
'|æ '# Push pair ["","|"] (powerset of "|")
}R # After the if-else statement: Reverse the pair
Xǝ # Insert it/them at index/indices `X` back into the string
# (after the loop, the result is output implicitly)
Setanta, 193 bytes
gniomh(s,m){s=roinn@s("|")t:=s[1]s=s[0]le i idir(0,fad@m){i=m[i]u:=fad@s ma i=="<"&u{t=s[-1]+t s=cuid@s(0,u-1)}ma i==">"&fad@t{s+=t[0]t=cuid@t(1,fad@t)}ma i=="#" s=cuid@s(0,u-1)}toradh s+"|"+t}
Charcoal, 50 bytes
≔⪪S¹θF⁺×<⌕⮌θ|⁺#S≡ι<«¿θ⊞υ⊟θ»>«¿υ⊞θ⊟υ»≔∧θ⊟θηFθι|Wυ⊟υ
Try it online! Link is to verbose version of code. Explanation:
≔⪪S¹θ
Input the text string and split it into a list of characters. At this point the program's cursor is still at the end of the string.
F⁺×<⌕⮌θ|⁺#S≡ι
Find the number of characters after the |, and prefix cursor commands to move the cursor back to the | and delete it to the command string, then loop over the resulting commands, switching over each character.
<«¿θ⊞υ⊟θ»
For a < try to move a character from the text string to the predefined empty list.
>«¿υ⊞θ⊟υ»
For a > try to move a character back to the text string.
≔∧θ⊟θη
Otherwise just try to delete a character from the text string.
Fθι|Wυ⊟υ
Reconstitute the string from the lists.
JavaScript (Node.js), 80 bytes
g=(x,[c,...d])=>c?g(x.replace(/(.?)\|(.?)/,c<g?'|$2':c>'='?'$1$2|':'|$1$2'),d):x
Perl 5 -p, 71 bytes
%C=qw(< (.)\|/|$1/ > \|(.)/$1|/ # .\|/|/);eval join";s/",@C{<>=~/^|./g}
same length:
%C=qw(< s/()(.)\| > s/\|(.) # s/.\|);$"='/$1|$2/;';eval"@C{<>=~/$|./g}"
Retina 0.8.2, 53 bytes
+`\|(.?)(.* )>|(.?)\|(.* )<|.?\|(.* )#
$1|$2$3$4$5
Try it online! Link includes test cases. Takes input separated by tabs (normally would have used newlines but that makes writing a test suite harder). Explanation: The replacement stage simply shuffles the | around depending on the next operation, with the + prefix repeating until no more operations remain, then finally the tab is deleted.
Python 3.8 (pre-release), 137 bytes
f=lambda s,n=0,*m:n and f([(x:=s[:(c:=s.find('|'))and~-c])+'|'+s[c-1:c]+s[c+1:],0,s[:c]+s[c+1:c+2]+'|'+s[c+2:],x+s[c:]][ord(n)%4],*m)or s
