Split a character string based on change of character: Difference between revisions
Not a robot (talk | contribs) Add Refal |
|||
(119 intermediate revisions by 59 users not shown) | |||
Line 1: | Line 1: | ||
[[Category: String manipulation]] |
[[Category: String manipulation]] |
||
[[Category:Strings]] |
|||
[[Category:Simple]] |
[[Category:Simple]] |
||
{{task}} |
{{task}} |
||
Line 23: | Line 24: | ||
<big><big> g, HHH, 5, YY, ++, ///, \ </big></big> |
<big><big> g, HHH, 5, YY, ++, ///, \ </big></big> |
||
{{Template:Strings}} |
|||
<br><br> |
<br><br> |
||
=={{header|11l}}== |
|||
{{trans|C++}} |
|||
<syntaxhighlight lang="11l">F split(input, delim) |
|||
V res = ‘’ |
|||
L(ch) input |
|||
I !res.empty & ch != res.last |
|||
res ‘’= delim |
|||
res ‘’= ch |
|||
R res |
|||
print(split(‘gHHH5YY++///\’, ‘, ’))</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
g, HHH, 5, YY, ++, ///, \ |
|||
</pre> |
|||
=={{header|8080 Assembly}}== |
|||
<syntaxhighlight lang="8080asm"> org 100h |
|||
jmp demo |
|||
;;; Split the string under DE on changing characters, |
|||
;;; and store the result at HL. |
|||
split: ldax d ; Load character from string |
|||
spcopy: mov m,a ; Store in output |
|||
cpi '$' ; CP/M string terminator |
|||
rz ; Stop when the end is reached |
|||
mov b,a ; Store previous character in B |
|||
inx d ; Increment input pointer |
|||
inx h ; Increment output pointer |
|||
ldax d ; Get next character |
|||
cmp b ; Same as previous character? |
|||
jz spcopy ; Then just copy it |
|||
cpi '$' ; Otherwise, if it is the en |
|||
jz spcopy ; Then just copy it as well |
|||
mvi m,',' ; Otherwise, add a comma and a space |
|||
inx h |
|||
mvi m,' ' |
|||
inx h |
|||
jmp spcopy |
|||
;;; Demo code |
|||
demo: lxi d,string |
|||
lxi h,out |
|||
call split ; Split the string |
|||
lxi d,out |
|||
mvi c,9 ; And print it using CP/M |
|||
jmp 5 |
|||
string: db 'gHHH5YY++///',5Ch,'$' |
|||
out: equ $</syntaxhighlight> |
|||
{{out}} |
|||
<pre>g, HHH, 5, YY, ++, ///, \</pre> |
|||
=={{header|8086 Assembly}}== |
|||
<syntaxhighlight lang="asm"> cpu 8086 |
|||
org 100h |
|||
section .text |
|||
jmp demo |
|||
;;; Split the string at DS:SI on changing characters, |
|||
;;; and store the result at ES:DI. |
|||
split: lodsb ; Load character |
|||
.copy: stosb ; Store in output |
|||
cmp al,'$' ; Done yet? |
|||
je .out ; If so, stop. |
|||
mov ah,al ; Store previous character |
|||
lodsb ; Get next character |
|||
cmp al,ah ; Same character? |
|||
je .copy ; Then just copy it |
|||
cmp al,'$' ; End of string? |
|||
je .copy ; Then just copy it too |
|||
mov dl,al |
|||
mov ax,', ' ; Otherwise, add a comma and a space |
|||
stosw |
|||
mov al,dl |
|||
jmp .copy |
|||
.out: ret |
|||
;;; Demo code |
|||
demo: mov si,string |
|||
mov di,buf |
|||
call split ; Split the string |
|||
mov dx,buf |
|||
mov ah,9 |
|||
int 21h ; And print the result using DOS |
|||
ret |
|||
section .data |
|||
string: db 'gHHH5YY++///\$' |
|||
section .bss |
|||
buf: resb 32</syntaxhighlight> |
|||
{{out}} |
|||
<pre>g, HHH, 5, YY, ++, ///, \</pre> |
|||
=={{header|AArch64 Assembly}}== |
|||
{{works with|as|Raspberry Pi 3B version Buster 64 bits}} |
|||
<syntaxhighlight lang="aarch64 assembly"> |
|||
/* ARM assembly AARCH64 Raspberry PI 3B */ |
|||
/* program splitcar64.s */ |
|||
/*******************************************/ |
|||
/* Constantes file */ |
|||
/*******************************************/ |
|||
/* for this file see task include a file in language AArch64 assembly*/ |
|||
.include "../includeConstantesARM64.inc" |
|||
/*********************************/ |
|||
/* Initialized data */ |
|||
/*********************************/ |
|||
.data |
|||
szCarriageReturn: .asciz "\n" |
|||
szString1: .asciz "gHHH5YY++///\\" |
|||
/* IMPORTANT REMARK for compiler as |
|||
The way to get special characters into a string is to escape these characters: precede them |
|||
with a backslash ‘\’ character. For example ‘\\’ represents one backslash: the first \ is |
|||
an escape which tells as to interpret the second character literally as a backslash (which |
|||
prevents as from recognizing the second \ as an escape character). |
|||
*/ |
|||
/*********************************/ |
|||
/* UnInitialized data */ |
|||
/*********************************/ |
|||
.bss |
|||
sBuffer: .skip 100 |
|||
/*********************************/ |
|||
/* code section */ |
|||
/*********************************/ |
|||
.text |
|||
.global main |
|||
main: // entry of program |
|||
ldr x0,qAdrszString1 // input string address |
|||
ldr x1,qAdrsBuffer // output buffer address |
|||
bl split |
|||
ldr x0,qAdrsBuffer |
|||
bl affichageMess // display message |
|||
ldr x0,qAdrszCarriageReturn |
|||
bl affichageMess |
|||
100: // standard end of the program |
|||
mov x0,0 // return code |
|||
mov x8,EXIT // request to exit program |
|||
svc 0 // perform the system call |
|||
qAdrszString1: .quad szString1 |
|||
qAdrszCarriageReturn: .quad szCarriageReturn |
|||
qAdrsBuffer: .quad sBuffer |
|||
/******************************************************************/ |
|||
/* generate value */ |
|||
/******************************************************************/ |
|||
/* x0 contains the address of input string */ |
|||
/* x1 contains the address of output buffer */ |
|||
split: |
|||
stp x1,lr,[sp,-16]! // save registers |
|||
mov x4,0 // indice loop input string |
|||
mov x5,0 // indice buffer |
|||
ldrb w2,[x0,x4] // read first char in reg x2 |
|||
cbz x2,4f // if null -> end |
|||
strb w2,[x1,x5] // store char in buffer |
|||
add x5,x5,1 // increment location buffer |
|||
1: |
|||
ldrb w3,[x0,x4] //read char[x4] in reg x3 |
|||
cbz x3,4f // if null end |
|||
cmp x2,x3 // compare two characters |
|||
bne 2f |
|||
strb w3,[x1,x5] // = -> store char in buffer |
|||
b 3f // loop |
|||
2: |
|||
mov x2,',' // else store comma in buffer |
|||
strb w2,[x1,x5] // store char in buffer |
|||
add x5,x5,1 |
|||
mov x2,' ' // and store space in buffer |
|||
strb w2,[x1,x5] |
|||
add x5,x5,1 |
|||
strb w3,[x1,x5] // and store input char in buffer |
|||
mov x2,x3 // and maj x2 with new char |
|||
3: |
|||
add x5,x5,1 // increment indices |
|||
add x4,x4,1 |
|||
b 1b // and loop |
|||
4: |
|||
strb w3,[x1,x5] // store zero final in buffer |
|||
100: |
|||
ldp x1,lr,[sp],16 // restaur 2 registers |
|||
ret // return to address lr x30 |
|||
/********************************************************/ |
|||
/* File Include fonctions */ |
|||
/********************************************************/ |
|||
/* for this file see task include a file in language AArch64 assembly */ |
|||
.include "../includeARM64.inc" |
|||
</syntaxhighlight> |
|||
{{Output}}<pre> gg, HHH, 5, YY, ++, ///, \ </pre> |
|||
=={{header|Action!}}== |
|||
<syntaxhighlight lang="action!">PROC Split(CHAR ARRAY s) |
|||
BYTE i |
|||
CHAR curr,last |
|||
i=1 last=s(1) |
|||
Put('") |
|||
WHILE i<=s(0) |
|||
DO |
|||
curr=s(i) |
|||
IF curr#last THEN |
|||
Print(", ") |
|||
FI |
|||
Put(curr) |
|||
last=curr |
|||
i==+1 |
|||
OD |
|||
Put('") |
|||
RETURN |
|||
PROC Test(CHAR ARRAY s) |
|||
PrintF("Input: ""%S""%E",s) |
|||
Print("Split: ") Split(s) |
|||
PutE() PutE() |
|||
RETURN |
|||
PROC Main() |
|||
Test("gHHH5YY++///\") |
|||
Test("gHHH 5++,,,///\") |
|||
RETURN</syntaxhighlight> |
|||
{{out}} |
|||
[https://gitlab.com/amarok8bit/action-rosetta-code/-/raw/master/images/Split_a_character_string_based_on_change_of_character.png Screenshot from Atari 8-bit computer] |
|||
<pre> |
|||
Input: "gHHH5YY++///\" |
|||
Split: "g, HHH, 5, YY, ++, ///, \" |
|||
Input: "gHHH 5++,,,///\" |
|||
Split: "g, HHH, , 5, ++, ,,,, ///, \" |
|||
</pre> |
|||
=={{header|Ada}}== |
=={{header|Ada}}== |
||
< |
<syntaxhighlight lang="ada"> |
||
with Ada.Text_IO; |
with Ada.Text_IO; |
||
procedure Split is |
procedure Split is |
||
Line 42: | Line 280: | ||
Print_Tokens ("gHHH5YY+++"); |
Print_Tokens ("gHHH5YY+++"); |
||
end split; |
end split; |
||
</syntaxhighlight> |
|||
</lang> |
|||
=={{header|ALGOL 68}}== |
=={{header|ALGOL 68}}== |
||
<syntaxhighlight lang="algol68">BEGIN |
|||
{{works with|ALGOL 68G|Any - tested with release 2.8.3.win32}} |
|||
<lang algol68>BEGIN |
|||
# returns s with ", " added between each change of character # |
# returns s with ", " added between each change of character # |
||
PROC split on characters = ( STRING s )STRING: |
PROC split on characters = ( STRING s )STRING: |
||
Line 57: | Line 294: | ||
[ 3 * ( ( UPB s - LWB s ) + 1 ) ]CHAR result; |
[ 3 * ( ( UPB s - LWB s ) + 1 ) ]CHAR result; |
||
INT r pos := LWB result; |
INT r pos := LWB result; |
||
INT s pos := LWB s; |
|||
CHAR s char := s[ LWB s ]; |
CHAR s char := s[ LWB s ]; |
||
FOR s pos FROM LWB s TO UPB s DO |
FOR s pos FROM LWB s TO UPB s DO |
||
Line 75: | Line 311: | ||
print( ( split on characters( "gHHH5YY++///\" ), newline ) ) |
print( ( split on characters( "gHHH5YY++///\" ), newline ) ) |
||
END</ |
END</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
||
g, HHH, 5, YY, ++, ///, \ |
g, HHH, 5, YY, ++, ///, \ |
||
</pre> |
</pre> |
||
=={{header|Amazing Hopper}}== |
|||
VERSION 1: string |
|||
<syntaxhighlight lang="c"> |
|||
#include <basico.h> |
|||
#define INICIO 1 |
|||
#define CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\"+-/ \\:,;:_*" |
|||
algoritmo |
|||
objetivo = "gHHH5YY\"\"++ ///,,,\\", indice=0 |
|||
largo=0, sublargo=0, v=0 |
|||
#( largo = len(indice:=(onechar(CHARS,objetivo))) ) |
|||
t=0, nuevo="" |
|||
para cada caracter ( v, indice, largo ) |
|||
#(t = replicate(v, sublargo := ((poschar(INICIO, v, objetivo) - 1 ) ) )) |
|||
#(nuevo = cat( cat(nuevo, t), ", ")) |
|||
objetivo+=sublargo |
|||
siguiente |
|||
nuevo -= 2 |
|||
imprimir( "NEW STRING=\n", nuevo,NL) |
|||
terminar |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
$ hopper3 basica/splitrep.bas |
|||
NEW STRING= |
|||
g, HHH, 5, YY, "", ++, , ///, ,,,, \ |
|||
</pre> |
|||
VERSION 2: arrays |
|||
<syntaxhighlight lang="c"> |
|||
#include <basico.h> |
|||
#define INICIO 1 |
|||
#define CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\"+-/ \\:,;:_*" |
|||
algoritmo |
|||
objetivo = "gHHH5YY\"\"++ ///,,,,\\", indice=0 |
|||
largo=0, sublargo=0, lista={}, v=0 |
|||
#( largo = len(indice:=(onechar(CHARS,objetivo))) ) |
|||
para cada caracter ( v, indice, largo ) |
|||
#( replicate(v, sublargo := ((poschar(INICIO, v, objetivo) - 1 )))) |
|||
meter en( lista ) |
|||
objetivo+=sublargo |
|||
siguiente |
|||
imprimir( "LISTA=\n", lista, NL ) |
|||
terminar |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
$ hopper3 basica/splitrep2.bas |
|||
LISTA= |
|||
g,HHH,5,YY,"",++, ,///,,,,,,\ |
|||
</pre> |
|||
=={{header|ANSI BASIC}}== |
|||
{{works with|Decimal BASIC}} |
|||
<syntaxhighlight lang="basic">REM >split |
|||
DECLARE EXTERNAL FUNCTION FN_split$ |
|||
PRINT FN_split$( "gHHH5YY++///\" ) |
|||
END |
|||
EXTERNAL FUNCTION FN_split$( s$ ) |
|||
LET c$ = s$(1:1) |
|||
LET split$ = "" |
|||
FOR i = 1 TO LEN(s$) |
|||
LET d$ = s$(i:i) |
|||
IF d$ <> c$ THEN |
|||
LET split$ = split$ & ", " |
|||
LET c$ = d$ |
|||
END IF |
|||
LET split$ = split$ & d$ |
|||
NEXT i |
|||
LET FN_split$ = split$ |
|||
END FUNCTION</syntaxhighlight> |
|||
{{out}} |
|||
<pre>g, HHH, 5, YY, ++, ///, \</pre> |
|||
=={{header|APL}}== |
|||
{{works with|Dyalog APL}} |
|||
<syntaxhighlight lang="apl">split ← 2↓∘∊(⊂', '),¨(⊢≠¯1⌽⊢)⊂⊢</syntaxhighlight> |
|||
{{out}} |
|||
<pre> split 'gHHH5YY++///\' |
|||
g, HHH, 5, YY, ++, ///, \</pre> |
|||
=={{header|AppleScript}}== |
=={{header|AppleScript}}== |
||
===Functional=== |
|||
{{Trans|JavaScript}} |
{{Trans|JavaScript}} |
||
< |
<syntaxhighlight lang="applescript">intercalate(", ", ¬ |
||
map(curry(intercalate)'s |λ|(""), ¬ |
map(curry(intercalate)'s |λ|(""), ¬ |
||
group("gHHH5YY++///\\"))) |
group("gHHH5YY++///\\"))) |
||
Line 199: | Line 534: | ||
{} |
{} |
||
end if |
end if |
||
end tail</ |
end tail</syntaxhighlight> |
||
{{Out}} |
{{Out}} |
||
<pre>g, HHH, 5, YY, ++, ///, \</pre> |
<pre>g, HHH, 5, YY, ++, ///, \</pre> |
||
===Straightforward=== |
|||
(Also case-sensitve.) |
|||
<syntaxhighlight lang="applescript">on splitAtCharacterChanges(input) |
|||
set len to (count input) |
|||
if (len < 2) then return input |
|||
set chrs to input's characters |
|||
set currentChr to beginning of chrs |
|||
considering case |
|||
repeat with i from 2 to len |
|||
set thisChr to item i of chrs |
|||
if (thisChr is not currentChr) then |
|||
set item i of chrs to ", " & thisChr |
|||
set currentChr to thisChr |
|||
end if |
|||
end repeat |
|||
end considering |
|||
set astid to AppleScript's text item delimiters |
|||
set AppleScript's text item delimiters to "" |
|||
set output to chrs as text |
|||
set AppleScript's text item delimiters to astid |
|||
return output |
|||
end splitAtCharacterChanges |
|||
-- Test code: |
|||
splitAtCharacterChanges("gHHH5YY++///\\")</syntaxhighlight> |
|||
{{output}} |
|||
<syntaxhighlight lang="applescript">"g, HHH, 5, YY, ++, ///, \\"</syntaxhighlight> |
|||
===ASObjC=== |
|||
<syntaxhighlight lang="applescript">use AppleScript version "2.4" -- OS X 10.10 (Yosemite) or later |
|||
use framework "Foundation" |
|||
on splitAtCharacterChanges(input) |
|||
tell (current application's class "NSMutableString"'s stringWithString:(input)) to ¬ |
|||
return (its stringByReplacingOccurrencesOfString:("(.)\\1*+(?!$)") withString:("$0, ") ¬ |
|||
options:(current application's NSRegularExpressionSearch) range:({0, its |length|()})) as text |
|||
end splitAtCharacterChanges |
|||
-- Test code: |
|||
splitAtCharacterChanges("gHHH5YY++///\\")</syntaxhighlight> |
|||
{{output}} |
|||
<syntaxhighlight lang="applescript">"g, HHH, 5, YY, ++, ///, \\"</syntaxhighlight> |
|||
=={{header|ARM Assembly}}== |
|||
{{works with|as|Raspberry Pi}} |
|||
<syntaxhighlight lang="arm assembly"> |
|||
/* ARM assembly Raspberry PI */ |
|||
/* program splitcar.s */ |
|||
/************************************/ |
|||
/* Constantes */ |
|||
/************************************/ |
|||
.equ STDOUT, 1 @ Linux output console |
|||
.equ EXIT, 1 @ Linux syscall |
|||
.equ WRITE, 4 @ Linux syscall |
|||
/*********************************/ |
|||
/* Initialized data */ |
|||
/*********************************/ |
|||
.data |
|||
szCarriageReturn: .asciz "\n" |
|||
szString1: .asciz "gHHH5YY++///\\" |
|||
/* IMPORTANT REMARK for compiler as |
|||
The way to get special characters into a string is to escape these characters: precede them |
|||
with a backslash ‘\’ character. For example ‘\\’ represents one backslash: the first \ is |
|||
an escape which tells as to interpret the second character literally as a backslash (which |
|||
prevents as from recognizing the second \ as an escape character). |
|||
*/ |
|||
/*********************************/ |
|||
/* UnInitialized data */ |
|||
/*********************************/ |
|||
.bss |
|||
sBuffer: .skip 100 |
|||
/*********************************/ |
|||
/* code section */ |
|||
/*********************************/ |
|||
.text |
|||
.global main |
|||
main: @ entry of program |
|||
ldr r0,iAdrszString1 @ input string address |
|||
ldr r1,iAdrsBuffer @ output buffer address |
|||
bl split |
|||
ldr r0,iAdrsBuffer |
|||
bl affichageMess @ display message |
|||
ldr r0,iAdrszCarriageReturn |
|||
bl affichageMess |
|||
100: @ standard end of the program |
|||
mov r0, #0 @ return code |
|||
mov r7, #EXIT @ request to exit program |
|||
svc #0 @ perform the system call |
|||
iAdrszString1: .int szString1 |
|||
iAdrszCarriageReturn: .int szCarriageReturn |
|||
iAdrsBuffer: .int sBuffer |
|||
/******************************************************************/ |
|||
/* generate value */ |
|||
/******************************************************************/ |
|||
/* r0 contains the address of input string */ |
|||
/* r1 contains the address of output buffer */ |
|||
split: |
|||
push {r1-r5,lr} @ save registers |
|||
mov r4,#0 @ indice loop input string |
|||
mov r5,#0 @ indice buffer |
|||
ldrb r2,[r0,r4] @ read first char in reg r2 |
|||
cmp r2,#0 @ if null -> end |
|||
beq 3f |
|||
strb r2,[r1,r5] @ store char in buffer |
|||
add r5,#1 @ increment location buffer |
|||
1: |
|||
ldrb r3,[r0,r4] @read char[r4] in reg r3 |
|||
cmp r3,#0 @ if null end |
|||
beq 3f |
|||
cmp r2,r3 @ compare two characters |
|||
streqb r3,[r1,r5] @ = -> store char in buffer |
|||
beq 2f @ loop |
|||
mov r2,#',' @ else store comma in buffer |
|||
strb r2,[r1,r5] @ store char in buffer |
|||
add r5,#1 |
|||
mov r2,#' ' @ and store space in buffer |
|||
strb r2,[r1,r5] |
|||
add r5,#1 |
|||
strb r3,[r1,r5] @ and store input char in buffer |
|||
mov r2,r3 @ and maj r2 with new char |
|||
2: |
|||
add r5,#1 @ increment indices |
|||
add r4,#1 |
|||
b 1b @ and loop |
|||
3: |
|||
strb r3,[r1,r5] @ store zero final in buffer |
|||
100: |
|||
pop {r1-r5,lr} |
|||
bx lr @ return |
|||
/******************************************************************/ |
|||
/* display text with size calculation */ |
|||
/******************************************************************/ |
|||
/* r0 contains the address of the message */ |
|||
affichageMess: |
|||
push {r0,r1,r2,r7,lr} @ save registres |
|||
mov r2,#0 @ counter length |
|||
1: @ loop length calculation |
|||
ldrb r1,[r0,r2] @ read octet start position + index |
|||
cmp r1,#0 @ if 0 its over |
|||
addne r2,r2,#1 @ else add 1 in the length |
|||
bne 1b @ and loop |
|||
@ so here r2 contains the length of the message |
|||
mov r1,r0 @ address message in r1 |
|||
mov r0,#STDOUT @ code to write to the standard output Linux |
|||
mov r7, #WRITE @ code call system "write" |
|||
svc #0 @ call systeme |
|||
pop {r0,r1,r2,r7,lr} @ restaur des 2 registres */ |
|||
bx lr @ return |
|||
output : gg, HHH, 5, YY, ++, ///, \ |
|||
</syntaxhighlight> |
|||
=={{header|Arturo}}== |
|||
<syntaxhighlight lang="rebol">parts: [] current: "" |
|||
loop split {gHHH5YY++///\} 'ch [ |
|||
if? or? empty? current |
|||
contains? current ch -> 'current ++ ch |
|||
else [ |
|||
'parts ++ current |
|||
current: new ch |
|||
] |
|||
] |
|||
'parts ++ current |
|||
print parts</syntaxhighlight> |
|||
{{out}} |
|||
<pre>g HHH 5 YY ++ /// \</pre> |
|||
=={{header|AutoHotkey}}== |
|||
<syntaxhighlight lang="autohotkey">Split_Change(str){ |
|||
for i, v in StrSplit(str) |
|||
res .= (v=prev) ? v : (res?", " :"") v , prev := v |
|||
return res |
|||
}</syntaxhighlight> |
|||
Examples:<syntaxhighlight lang="autohotkey">str := "gHHH5YY++///\" |
|||
MsgBox % Split_Change(str)</syntaxhighlight> |
|||
Outputs:<pre>g, HHH, 5, YY, ++, ///, \</pre> |
|||
===RegEx Version=== |
|||
<syntaxhighlight lang="autohotkey">Split_Change(str){ |
|||
return RegExReplace(str, "(.)\1*(?!$)", "$0, ") |
|||
}</syntaxhighlight> |
|||
Examples:<syntaxhighlight lang="autohotkey">str := "gHHH5YY++///\" |
|||
MsgBox % Split_Change(str)</syntaxhighlight> |
|||
Outputs:<pre>g, HHH, 5, YY, ++, ///, \</pre> |
|||
=={{header|AWK}}== |
=={{header|AWK}}== |
||
<syntaxhighlight lang="awk"> |
|||
<lang AWK> |
|||
# syntax: GAWK -f SPLIT_A_CHARACTER_STRING_BASED_ON_CHANGE_OF_CHARACTER.AWK |
# syntax: GAWK -f SPLIT_A_CHARACTER_STRING_BASED_ON_CHANGE_OF_CHARACTER.AWK |
||
BEGIN { |
BEGIN { |
||
Line 222: | Line 763: | ||
return(new_str) |
return(new_str) |
||
} |
} |
||
</syntaxhighlight> |
|||
</lang> |
|||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
||
Line 231: | Line 772: | ||
=={{header|BaCon}}== |
=={{header|BaCon}}== |
||
Literal strings in BaCon are passed to the C compiler as they are; a backslash therefore needs to be escaped. |
Literal strings in BaCon are passed to the C compiler as they are; a backslash therefore needs to be escaped. |
||
< |
<syntaxhighlight lang="freebasic">txt$ = "gHHH5YY++///\\" |
||
c$ = LEFT$(txt$, 1) |
c$ = LEFT$(txt$, 1) |
||
Line 242: | Line 783: | ||
END IF |
END IF |
||
PRINT d$; |
PRINT d$; |
||
NEXT</ |
NEXT</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
||
g, HHH, 5, YY, ++, ///, \ |
g, HHH, 5, YY, ++, ///, \ |
||
</pre> |
</pre> |
||
=={{header|BASIC256}}== |
|||
<syntaxhighlight lang="freebasic">function split$(instring$) |
|||
if length(instring$) < 2 then return instring$ |
|||
ret$ = left(instring$,1) |
|||
for i = 2 to length(instring$) |
|||
if mid(instring$,i,1) <> mid(instring$, i-1, 1) then ret$ += ", " |
|||
ret$ += mid(instring$, i, 1) |
|||
next i |
|||
return ret$ |
|||
end function |
|||
print split$("gHHH5YY++///\")</syntaxhighlight> |
|||
=={{header|BBC BASIC}}== |
=={{header|BBC BASIC}}== |
||
< |
<syntaxhighlight lang="bbcbasic">REM >split |
||
PRINT FN_split( "gHHH5YY++///\" ) |
PRINT FN_split( "gHHH5YY++///\" ) |
||
END |
END |
||
DEF FN_split |
DEF FN_split( s$ ) |
||
LOCAL c$, d$, i% |
LOCAL c$, split$, d$, i% |
||
c$ = LEFT$( s$, 1 ) |
c$ = LEFT$( s$, 1 ) |
||
split$ = "" |
split$ = "" |
||
Line 265: | Line 819: | ||
split$ += d$ |
split$ += d$ |
||
NEXT |
NEXT |
||
= split$</ |
= split$</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre>g, HHH, 5, YY, ++, ///, \</pre> |
<pre>g, HHH, 5, YY, ++, ///, \</pre> |
||
=={{header|ANSI BASIC}}== |
|||
<lang ansibasic>REM >split |
|||
DECLARE EXTERNAL FUNCTION FN_split$ |
|||
=={{header|BQN}}== |
|||
PRINT FN_split$( "gHHH5YY++///\" ) |
|||
<syntaxhighlight lang="bqn">Split ← (+`⊏⊸»⊸≠)⊸⊔ |
|||
END |
|||
Join ← {∾⟜𝕨⊸∾´𝕩} |
|||
", " Join⟜Split "gHHH5YY++///\"</syntaxhighlight> |
|||
EXTERNAL FUNCTION FN_split$( s$ ) |
|||
LET c$ = s$(1:1) |
|||
LET split$ = "" |
|||
FOR i = 1 TO LEN(s$) |
|||
LET d$ = s$(i:i) |
|||
IF d$ <> c$ THEN |
|||
LET split$ = split$ & ", " |
|||
LET c$ = d$ |
|||
END IF |
|||
LET split$ = split$ & d$ |
|||
NEXT i |
|||
LET FN_split$ = split$</lang> |
|||
{{out}} |
{{out}} |
||
<pre>g, HHH, 5, YY, ++, ///, \</pre> |
<pre>"g, HHH, 5, YY, ++, ///, \"</pre> |
||
=={{header|C}}== |
=={{header|C}}== |
||
< |
<syntaxhighlight lang="c">#include <stdio.h> |
||
#include <stdlib.h> |
#include <stdlib.h> |
||
#include <string.h> |
#include <string.h> |
||
Line 314: | Line 855: | ||
*(counter--)='\0'; |
*(counter--)='\0'; |
||
return realloc(result,strlen(result)); |
return realloc(result,strlen(result)); |
||
}</ |
}</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
||
Line 321: | Line 862: | ||
=={{header|C sharp}}== |
=={{header|C sharp}}== |
||
< |
<syntaxhighlight lang="csharp">using System; |
||
using System.Linq; |
using System.Linq; |
||
using System.Collections.Generic; |
using System.Collections.Generic; |
||
Line 352: | Line 893: | ||
public static string Delimit<T>(this IEnumerable<T> source, string separator = "") => string.Join(separator ?? "", source); |
public static string Delimit<T>(this IEnumerable<T> source, string separator = "") => string.Join(separator ?? "", source); |
||
}</ |
}</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
||
Line 359: | Line 900: | ||
=={{header|C++}}== |
=={{header|C++}}== |
||
< |
<syntaxhighlight lang="cpp"> |
||
// Solution for http://rosettacode.org/wiki/Split_a_character_string_based_on_change_of_character |
// Solution for http://rosettacode.org/wiki/Split_a_character_string_based_on_change_of_character |
||
#include<string> |
#include<string> |
||
#include<iostream> |
#include<iostream> |
||
auto spliter(const std::string &input) { |
|||
auto split(const std::string& input, const std::string& delim){ |
|||
auto firstCommaPast = false; |
|||
std::string res; |
|||
for(auto ch : input){ |
|||
if(!res.empty() && ch != res.back()) |
|||
res += delim; |
|||
if(*it!=prev) { |
|||
res += ch; |
|||
if(!firstCommaPast) { |
|||
} |
|||
firstCommaPast = true; |
|||
return res; |
|||
} else { |
|||
res+=", "; |
|||
} |
|||
} |
|||
res+=*it; |
|||
prev=*it; |
|||
} |
|||
return res; |
|||
} |
} |
||
int main() |
int main(){ |
||
std::cout << split("gHHH5 ))YY++,,,///\\", ", ") << std::endl; |
|||
}</syntaxhighlight> |
|||
std::cout<<spliter(input); |
|||
} </lang> |
|||
{{out}} |
{{out}} |
||
<pre>g, HHH, 5, , )), YY, ++, ,,,, ///, \</pre> |
<pre>g, HHH, 5, , )), YY, ++, ,,,, ///, \</pre> |
||
=={{header|Clojure}}== |
=={{header|Clojure}}== |
||
< |
<syntaxhighlight lang="clojure">(defn print-cchanges [s] |
||
(println (clojure.string/join ", " (map first (re-seq #"(.)\1*" s))))) |
(println (clojure.string/join ", " (map first (re-seq #"(.)\1*" s))))) |
||
(print-cchanges "gHHH5YY++///\\") |
(print-cchanges "gHHH5YY++///\\") |
||
</syntaxhighlight> |
|||
</lang> |
|||
{{out}} |
{{out}} |
||
<pre>g, HHH, 5, YY, ++, ///, \</pre> |
<pre>g, HHH, 5, YY, ++, ///, \</pre> |
||
=={{header|CLU}}== |
|||
<syntaxhighlight lang="clu">% Split a string based on a change of character |
|||
split_on_change = iter (s: string) yields (string) |
|||
part: string := "" |
|||
for c: char in string$chars(s) do |
|||
if ~string$empty(part) |
|||
cand part[string$size(part)] ~= c then |
|||
yield(part) |
|||
part := "" |
|||
end |
|||
part := part || string$c2s(c) |
|||
end |
|||
yield(part) |
|||
end split_on_change |
|||
start_up = proc () |
|||
po: stream := stream$primary_output() |
|||
str: string := "gHHH5YYY++///\\" % \\ escapes, as in C |
|||
rslt: string := "" |
|||
first: bool := true |
|||
for part: string in split_on_change(str) do |
|||
if first then first := false |
|||
else rslt := rslt || ", " |
|||
end |
|||
rslt := rslt || part |
|||
end |
|||
stream$putl(po, rslt) |
|||
end start_up</syntaxhighlight> |
|||
{{out}} |
|||
<pre>g, HHH, 5, YYY, ++, ///, \</pre> |
|||
=={{header|COBOL}}== |
=={{header|COBOL}}== |
||
<syntaxhighlight lang="cobol"> |
|||
<lang COBOL> |
|||
identification division. |
identification division. |
||
program-id. split-ch. |
program-id. split-ch. |
||
Line 460: | Line 1,025: | ||
end program split-ch. |
end program split-ch. |
||
</syntaxhighlight> |
|||
</lang> |
|||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
||
Line 474: | Line 1,039: | ||
=={{header|Common Lisp}}== |
=={{header|Common Lisp}}== |
||
< |
<syntaxhighlight lang="lisp">(defun split (string) |
||
(loop :for prev := nil :then c |
(loop :for prev := nil :then c |
||
:for c :across string |
:for c :across string |
||
Line 480: | Line 1,045: | ||
(split "gHHH5YY++///\\") |
(split "gHHH5YY++///\\") |
||
</syntaxhighlight> |
|||
</lang> |
|||
{{out}} |
{{out}} |
||
<pre>g, HHH, 5, YY, ++, ///, \</pre> |
<pre>g, HHH, 5, YY, ++, ///, \</pre> |
||
Line 486: | Line 1,051: | ||
Doing more work that what's being ask, the following solution builds a list of strings then output it: |
Doing more work that what's being ask, the following solution builds a list of strings then output it: |
||
< |
<syntaxhighlight lang="lisp">(defun split (string) |
||
(flet ((make-buffer () |
(flet ((make-buffer () |
||
(make-array 0 :element-type 'character :adjustable t :fill-pointer t))) |
(make-array 0 :element-type 'character :adjustable t :fill-pointer t))) |
||
Line 500: | Line 1,065: | ||
(format t "~{~A~^, ~}"(nreverse result))))) |
(format t "~{~A~^, ~}"(nreverse result))))) |
||
(split "gHHH5YY++///\\")</ |
(split "gHHH5YY++///\\")</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre>g, HHH, 5, YY, ++, ///, \</pre> |
<pre>g, HHH, 5, YY, ++, ///, \</pre> |
||
=={{header| |
=={{header|Cowgol}}== |
||
<syntaxhighlight lang="cowgol">include "cowgol.coh"; |
|||
sub split(in: [uint8], buf: [uint8]): (out: [uint8]) is |
|||
{{output?}} |
|||
out := buf; |
|||
loop |
|||
[buf] := [in]; |
|||
if [in] == 0 then break; end if; |
|||
if [in] != [@next in] and [@next in] != 0 then |
|||
[buf+1] := ','; |
|||
[buf+2] := ' '; |
|||
buf := buf+2; |
|||
end if; |
|||
buf := buf+1; |
|||
in := in+1; |
|||
end loop; |
|||
end sub; |
|||
var buf: uint8[32]; |
|||
<lang D>import std.stdio; |
|||
print(split("gHHH5YY++//\\", &buf[0])); |
|||
print_nl();</syntaxhighlight> |
|||
{{out}} |
|||
<pre>g, HHH, 5, YY, ++, //, \</pre> |
|||
=={{header|D}}== |
|||
<syntaxhighlight lang="d">import std.stdio; |
|||
void main() { |
void main() { |
||
Line 522: | Line 1,110: | ||
} |
} |
||
writeln(); |
writeln(); |
||
}</ |
}</syntaxhighlight> |
||
{{output}} |
|||
<pre>g, HHH, 5, YY, ++, ///, \</pre> |
|||
=={{header|Delphi}}== |
|||
{{works with|Delphi|6.0}} |
|||
{{libheader|SysUtils,StdCtrls}} |
|||
<syntaxhighlight lang="Delphi"> |
|||
function SplitStringCharChange(S: string): string; |
|||
{Split string whenever the previous char is different from the current one} |
|||
var I: integer; |
|||
var C: char; |
|||
begin |
|||
Result:=''; |
|||
{Copy string to output} |
|||
for I:=1 to Length(S) do |
|||
begin |
|||
Result:=Result+S[I]; |
|||
{Appended ", " if the next char is different} |
|||
if (I<Length(S)) and (S[I]<>S[I+1]) then Result:=Result+', '; |
|||
end; |
|||
end; |
|||
procedure ShowSplitString(Memo: TMemo); |
|||
const S1 = 'gHHH5YY++///\'; |
|||
var S2: string; |
|||
begin |
|||
Memo.Lines.Add(S1); |
|||
S2:=SplitStringCharChange(S1); |
|||
Memo.Lines.Add(S2); |
|||
end; |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
gHHH5YY++///\ |
|||
g, HHH, 5, YY, ++, ///, \ |
|||
Elapsed Time: 1.767 ms. |
|||
</pre> |
|||
=={{header|Dyalect}}== |
|||
<syntaxhighlight lang="dyalect">func String.SmartSplit() { |
|||
var c |
|||
var str = "" |
|||
var last = this.Length() - 1 |
|||
for n in 0..last { |
|||
if c && this[n] != c { |
|||
str += ", " |
|||
} |
|||
c = this[n] |
|||
str += c |
|||
} |
|||
str |
|||
} |
|||
print("gHHH5YY++///\\".SmartSplit())</syntaxhighlight> |
|||
{{out}} |
|||
<pre>g, HHH, 5, YY, ++, ///, \</pre> |
|||
=={{header|EasyLang}}== |
|||
<syntaxhighlight lang="text"> |
|||
a$ = "gHHH5YY++///\\" |
|||
a$[] = strchars a$ |
|||
cp$ = a$[1] |
|||
for c$ in a$[] |
|||
if c$ <> cp$ |
|||
s$ &= ", " |
|||
cp$ = c$ |
|||
. |
|||
s$ &= c$ |
|||
. |
|||
print s$ |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
g, HHH, 5, YY, ++, ///, \ |
|||
</pre> |
|||
=={{header|ed}}== |
|||
<syntaxhighlight lang="sed"> |
|||
# by Artyom Bologov |
|||
H |
|||
# Repeating two times because matching is greedy |
|||
g/.*/s/((.)\2*)((.)\4*)/\1\ |
|||
\3/g |
|||
g/.*/s/((.)\2*)((.)\4*)/\1\ |
|||
\3/g |
|||
,p |
|||
Q |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre>$ cat split-on-change.ed | ed -lEGs split-on-change.input |
|||
Newline appended |
|||
g |
|||
HHH |
|||
5 |
|||
YY |
|||
++ |
|||
/// |
|||
\ |
|||
</pre> |
|||
=={{header|Elixir}}== |
=={{header|Elixir}}== |
||
< |
<syntaxhighlight lang="elixir">split = fn str -> |
||
IO.puts " input string: #{str}" |
IO.puts " input string: #{str}" |
||
String.graphemes(str) |
String.graphemes(str) |
||
Line 533: | Line 1,236: | ||
end |
end |
||
split.("gHHH5YY++///\\")</ |
split.("gHHH5YY++///\\")</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
Line 542: | Line 1,245: | ||
=={{header|F_Sharp|F#}}== |
=={{header|F_Sharp|F#}}== |
||
< |
<syntaxhighlight lang="fsharp">open System.Text.RegularExpressions |
||
let splitRuns s = Regex("""(.)\1*""").Matches(s) |> Seq.cast<Match> |> Seq.map (fun m -> m.Value) |> Seq.toList |
let splitRuns s = Regex("""(.)\1*""").Matches(s) |> Seq.cast<Match> |> Seq.map (fun m -> m.Value) |> Seq.toList |
||
printfn "%A" (splitRuns """gHHH5YY++///\""")</ |
printfn "%A" (splitRuns """gHHH5YY++///\""")</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre>["g"; "HHH"; "5"; "YY"; "++"; "///"; "\"]</pre> |
<pre>["g"; "HHH"; "5"; "YY"; "++"; "///"; "\"]</pre> |
||
=={{header|Factor}}== |
=={{header|Factor}}== |
||
< |
<syntaxhighlight lang="factor">USE: splitting.monotonic |
||
"gHHH5YY++///\\" |
"gHHH5YY++///\\" |
||
"aaabbccccdeeff" [ [ = ] monotonic-split ", " join print ] bi@</ |
"aaabbccccdeeff" [ [ = ] monotonic-split ", " join print ] bi@</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
||
Line 560: | Line 1,263: | ||
=={{header|Forth}}== |
=={{header|Forth}}== |
||
{{works with|Gforth|0.7.3}} |
{{works with|Gforth|0.7.3}} |
||
< |
<syntaxhighlight lang="forth">CREATE A 0 , |
||
: C@A+ A @ C@ [ 1 CHARS ]L A +! ; |
: C@A+ A @ C@ [ 1 CHARS ]L A +! ; |
||
: SPLIT. ( c-addr u --) SWAP A ! A @ C@ |
: SPLIT. ( c-addr u --) SWAP A ! A @ C@ |
||
Line 572: | Line 1,275: | ||
s" gHHH5YY++///\" TEST |
s" gHHH5YY++///\" TEST |
||
s" gHHH5 ))YY++,,,///\" TEST |
s" gHHH5 ))YY++,,,///\" TEST |
||
BYE</ |
BYE</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre>input: gHHH5YY++///\ |
<pre>input: gHHH5YY++///\ |
||
Line 586: | Line 1,289: | ||
If the problem were to be solved by writing a "main line" only, there would have to be a declaration of the text variable there but since a subroutine can receive a CHARACTER variable of any size (the actual size is passed as a secret parameter), this can be dodged. |
If the problem were to be solved by writing a "main line" only, there would have to be a declaration of the text variable there but since a subroutine can receive a CHARACTER variable of any size (the actual size is passed as a secret parameter), this can be dodged. |
||
For this example a DO-loop stepping along the text is convenient, but in a larger context it would probably be most useful to work along the text with fingers L1 and L2 marking the start and finish positions of each sequence. < |
For this example a DO-loop stepping along the text is convenient, but in a larger context it would probably be most useful to work along the text with fingers L1 and L2 marking the start and finish positions of each sequence. <syntaxhighlight lang="fortran"> SUBROUTINE SPLATTER(TEXT) !Print a comma-separated list. Repeated characters constitute one item. |
||
Can't display the inserted commas in a different colour so as not to look like any commas in TEXT. |
Can't display the inserted commas in a different colour so as not to look like any commas in TEXT. |
||
CHARACTER*(*) TEXT !The text. |
CHARACTER*(*) TEXT !The text. |
||
Line 606: | Line 1,309: | ||
PROGRAM POKE |
PROGRAM POKE |
||
CALL SPLATTER("gHHH5YY++///\") !The example given. |
CALL SPLATTER("gHHH5YY++///\") !The example given. |
||
END</ |
END</syntaxhighlight> |
||
Unfortunately, the syntax highlighter has failed to notice the terminating quote character, presumably because the preceding backslash might be an "escape sequence" trigger, a facility ''not'' used in Fortran text ''literals'' except possibly as a later modernist option. |
Unfortunately, the syntax highlighter has failed to notice the terminating quote character, presumably because the preceding backslash might be an "escape sequence" trigger, a facility ''not'' used in Fortran text ''literals'' except possibly as a later modernist option. |
||
{{Out}} |
{{Out}} |
||
<pre> |
|||
g, HHH, 5, YY, ++, ///, \ |
|||
</pre> |
|||
=={{header|FreeBASIC}}== |
|||
<syntaxhighlight lang="freebasic">function split( instring as string ) as string |
|||
if len(instring) < 2 then return instring |
|||
dim as string ret = left(instring,1) |
|||
for i as uinteger = 2 to len(instring) |
|||
if mid(instring,i,1)<>mid(instring, i - 1, 1) then ret + = ", " |
|||
ret += mid(instring, i, 1) |
|||
next i |
|||
return ret |
|||
end function</syntaxhighlight> |
|||
=={{header|Frink}}== |
|||
<syntaxhighlight lang="frink">s = "gHHH5YY++///\\" |
|||
println[join[", ", map[getFunction["first", 1], s =~ %r/((.)\2*)/g]]]</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
g, HHH, 5, YY, ++, ///, \ |
|||
</pre> |
|||
=={{header|FutureBasic}}== |
|||
FB can process either Pascal strings (slowly being deprecated), or Apple's Core Foundation CFStrings (and Objective-C NSStrings). Here's the old-school Pascal string function: |
|||
<syntaxhighlight lang="text"> |
|||
local fn SplitString( inputStr as Str255 ) as Str255 |
|||
Str255 resultStr |
|||
NSUInteger i |
|||
if len$( inputStr ) < 2 then resultStr = inputStr : exit fn |
|||
resultStr = left$( inputStr, 1 ) |
|||
for i = 2 to len$( inputStr ) |
|||
if mid$( inputStr, i, 1 ) <> mid$( inputStr, i - 1, 1 ) then resultStr = resultStr + ", " |
|||
resultStr = resultStr + mid$(inputStr, i, 1) |
|||
next |
|||
end fn = resultStr |
|||
window 1 |
|||
print fn SplitString( "gHHH5YY++///\" ) |
|||
HandleEvents |
|||
</syntaxhighlight> |
|||
And here's the recommended CFString counterpart: |
|||
<syntaxhighlight lang="text"> |
|||
local fn SplitString( inputStr as CFStringRef ) as CFStringRef |
|||
NSUInteger i |
|||
unichar chr, lastChr = fn StringCharacterAtIndex( inputStr, 0 ) |
|||
CFMutableStringRef resultStr = fn MutableStringWithCapacity(0) |
|||
for i = 0 to len( inputStr ) - 1 |
|||
chr = fn StringCharacterAtIndex( inputStr, i ) |
|||
if ( chr != lastChr ) then MutableStringAppendString( resultStr, @", " ) |
|||
MutableStringAppendString( resultStr, mid( inputStr, i, 1 ) ) |
|||
lastChr = chr |
|||
next |
|||
end fn = resultStr |
|||
window 1 |
|||
print fn SplitString( @"gHHH5YY++///\\" ) |
|||
HandleEvents |
|||
</syntaxhighlight> |
|||
'''Output for either function:''' |
|||
<pre> |
<pre> |
||
g, HHH, 5, YY, ++, ///, \ |
g, HHH, 5, YY, ++, ///, \ |
||
Line 616: | Line 1,385: | ||
=={{header|Go}}== |
=={{header|Go}}== |
||
Treating "character" as a byte: |
Treating "character" as a byte: |
||
< |
<syntaxhighlight lang="go">package main |
||
import ( |
import ( |
||
Line 642: | Line 1,411: | ||
} |
} |
||
return b.String() |
return b.String() |
||
}</ |
}</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
||
g, HHH, 5, YY, ++, ///, \ |
g, HHH, 5, YY, ++, ///, \ |
||
</pre> |
</pre> |
||
=={{header|Haskell}}== |
=={{header|Haskell}}== |
||
< |
<syntaxhighlight lang="haskell">import Data.List (group, intercalate) |
||
main :: IO () |
main :: IO () |
||
main = putStrLn $ intercalate ", " (group "gHHH5YY++///\\")</ |
main = putStrLn $ intercalate ", " (group "gHHH5YY++///\\")</syntaxhighlight> |
||
{{Out}} |
{{Out}} |
||
<pre>g, HHH, 5, YY, ++, ///, \</pre> |
<pre>g, HHH, 5, YY, ++, ///, \</pre> |
||
or as a hand-written fold: |
|||
<syntaxhighlight lang="haskell">import Data.List (intercalate) |
|||
import Data.Bool (bool) |
|||
charGroups :: String -> [String] |
|||
charGroups = |
|||
let go (a, b) (s, groups) |
|||
| a == b = (b : s, groups) |
|||
| otherwise = |
|||
( [a], |
|||
bool s [b] (null s) : groups |
|||
) |
|||
in uncurry (:) . foldr go ([], []) . (zip <*> tail) |
|||
main :: IO () |
|||
main = |
|||
putStrLn $ intercalate ", " $ charGroups "gHHH5YY++///\\"</syntaxhighlight> |
|||
<pre>g, HHH, 5, YY, ++, ///, \</pre> |
|||
or in terms of '''span''': |
|||
<syntaxhighlight lang="haskell">import Data.List (intercalate) |
|||
charGroups :: String -> [String] |
|||
charGroups [] = [] |
|||
charGroups (c : cs) = |
|||
let (xs, ys) = span (c ==) cs |
|||
in (c : xs) : charGroups ys |
|||
main :: IO () |
|||
main = |
|||
putStrLn $ intercalate ", " $ charGroups "gHHH5YY++///\\"</syntaxhighlight> |
|||
{{Out}} |
|||
<pre>g, HHH, 5, YY, ++, ///, \</pre> |
|||
=={{header|IS-BASIC}}== |
|||
<syntaxhighlight lang="is-basic">100 LET S$="gHHH5YY++///\" |
|||
110 PRINT S$(1); |
|||
120 FOR I=2 TO LEN(S$) |
|||
130 IF S$(I)<>S$(I-1) THEN PRINT ", "; |
|||
140 PRINT S$(I); |
|||
150 NEXT |
|||
160 PRINT</syntaxhighlight> |
|||
=={{header|J}}== |
=={{header|J}}== |
||
'''Solution:''' |
'''Solution:''' |
||
< |
<syntaxhighlight lang="j">splitChars=: (1 ,~ 2 ~:/\ ]) <;.2 ] |
||
delimitChars=: ', ' joinstring splitChars</ |
delimitChars=: ', ' joinstring splitChars</syntaxhighlight> |
||
'''Example Usage:''' |
'''Example Usage:''' |
||
< |
<syntaxhighlight lang="j"> delimitChars 'gHHH5YY++///\' |
||
g, HHH, 5, YY, ++, ///, \</ |
g, HHH, 5, YY, ++, ///, \</syntaxhighlight> |
||
=={{header|Java}}== |
=={{header|Java}}== |
||
You can use a regular expression to capture every character preceded by 0 or more of itself. |
|||
<syntaxhighlight lang="java"> |
|||
<lang Java>package org.rosettacode; |
|||
import java.util.regex.Matcher; |
|||
import java.util.regex.Pattern; |
|||
</syntaxhighlight> |
|||
<syntaxhighlight lang="java"> |
|||
String split(String string) { |
|||
Pattern pattern = Pattern.compile("(.)\\1*"); |
|||
Matcher matcher = pattern.matcher(string); |
|||
StringBuilder strings = new StringBuilder(); |
|||
int index = 0; |
|||
while (matcher.find()) { |
|||
if (index++ != 0) |
|||
strings.append(", "); |
|||
strings.append(matcher.group()); |
|||
} |
|||
return strings.toString(); |
|||
} |
|||
</syntaxhighlight> |
|||
<pre> |
|||
g, HHH, 5, YY, ++, ///, \ |
|||
</pre> |
|||
<br /> |
|||
An alternate demonstration |
|||
<syntaxhighlight lang="java">package org.rosettacode; |
|||
import java.util.ArrayList; |
import java.util.ArrayList; |
||
Line 732: | Line 1,568: | ||
return output.toString(); |
return output.toString(); |
||
} |
} |
||
}</ |
}</syntaxhighlight> |
||
{{Out}} |
{{Out}} |
||
Line 740: | Line 1,576: | ||
===ES6=== |
===ES6=== |
||
{{Trans|Haskell}} |
{{Trans|Haskell}} |
||
< |
<syntaxhighlight lang="javascript">(() => { |
||
"use strict"; |
|||
// GENERIC FUNCTIONS ------------------------------------------------------ |
|||
// ----------- SPLIT ON CHARACTER CHANGES ------------ |
|||
// concat :: [[a]] -> [a] | [String] -> String |
|||
const |
const main = () => |
||
group("gHHH5YY++///\\") |
|||
.map(x => x.join("")) |
|||
const unit = typeof xs[0] === 'string' ? '' : []; |
|||
.join(", "); |
|||
// --------------------- GENERIC --------------------- |
|||
// group :: [a] -> [[a]] |
|||
const group = xs => |
|||
// A list of lists, each containing only |
|||
// elements equal under (===), such that the |
|||
// concatenation of these lists is xs. |
|||
groupBy(a => b => a === b)(xs); |
|||
// groupBy :: (a -> a -> Bool) [a] -> [[a]] |
|||
const groupBy = eqOp => |
|||
// A list of lists, each containing only elements |
|||
// equal under the given equality operator, |
|||
// such that the concatenation of these lists is xs. |
|||
xs => 0 < xs.length ? (() => { |
|||
const [h, ...t] = xs; |
|||
const [groups, g] = t.reduce( |
|||
([gs, a], x) => eqOp(x)(a[0]) ? ( |
|||
Tuple(gs)([...a, x]) |
|||
) : Tuple([...gs, a])([x]), |
|||
Tuple([])([h]) |
|||
); |
|||
return [...groups, g]; |
|||
})() : []; |
})() : []; |
||
// group :: Eq a => [a] -> [[a]] |
|||
const group = xs => groupBy((a, b) => a === b, xs); |
|||
// |
// Tuple (,) :: a -> b -> (a, b) |
||
const |
const Tuple = a => |
||
b => ({ |
|||
type: "Tuple", |
|||
"0": a, |
|||
"1": b, |
|||
length: 2, |
|||
*[Symbol.iterator]() { |
|||
for (const k in this) { |
|||
if (!isNaN(k)) { |
|||
yield this[k]; |
|||
} |
|||
} |
|||
} |
|||
}); |
|||
return dct.sofar.concat(dct.active.length > 0 ? [dct.active] : []); |
|||
// MAIN --- |
|||
return main(); |
|||
})();</syntaxhighlight> |
|||
{{Out}} |
|||
<pre>g, HHH, 5, YY, ++, ///, \</pre> |
|||
Or, in terms of a general `span` function: |
|||
<syntaxhighlight lang="javascript">(() => { |
|||
"use strict"; |
|||
// -------- STRING SPLIT ON CHARACTER CHANGES -------- |
|||
// charGroups :: String -> [String] |
|||
const charGroups = s => |
|||
// The characters of s split at each point where |
|||
// consecutive characters differ. |
|||
0 < s.length ? (() => { |
|||
const |
|||
c = s[0], |
|||
[xs, ys] = span(x => c === x)([ |
|||
...s.slice(1) |
|||
]); |
|||
return [ |
|||
[c, ...xs], ...charGroups(ys) |
|||
] |
|||
.map(zs => [...zs].join("")); |
|||
})() : ""; |
|||
// intercalate :: String -> [a] -> String |
|||
const intercalate = (s, xs) => xs.join(s); |
|||
// ---------------------- TEST ----------------------- |
|||
// map :: (a -> b) -> [a] -> [b] |
|||
// main :: IO() |
|||
const main = () => |
|||
charGroups("gHHH5YY++///\\") |
|||
.join(", "); |
|||
// show :: a -> String |
|||
const show = (...x) => |
|||
JSON.stringify.apply( |
|||
null, x.length > 1 ? [x[0], null, x[1]] : x |
|||
); |
|||
// --------------------- GENERIC --------------------- |
|||
// stringChars :: String -> [Char] |
|||
const stringChars = s => s.split(''); |
|||
// span :: (a -> Bool) -> [a] -> ([a], [a]) |
|||
const span = p => |
|||
// Longest prefix of xs consisting of elements which |
|||
// all satisfy p, tupled with the remainder of xs. |
|||
xs => { |
|||
const i = xs.findIndex(x => !p(x)); |
|||
return -1 !== i ? [ |
|||
// TEST ------------------------------------------------------------------- |
|||
xs.slice(0, i), |
|||
return show( |
|||
xs.slice(i) |
|||
] : [xs, []]; |
|||
}; |
|||
); |
|||
// MAIN --- |
|||
// -> "g, HHH, 5, YY, ++, ///, \\" |
|||
return main(); |
|||
})();</lang> |
|||
})();</syntaxhighlight> |
|||
{{Out}} |
{{Out}} |
||
<pre>g, HHH, 5, YY, ++, ///, \</pre> |
<pre>g, HHH, 5, YY, ++, ///, \</pre> |
||
=={{header|jq}}== |
=={{header|jq}}== |
||
< |
<syntaxhighlight lang="jq"># input: a string |
||
# output: a stream of runs |
# output: a stream of runs |
||
def runs: |
def runs: |
||
Line 812: | Line 1,702: | ||
end; |
end; |
||
"gHHH5YY++///\\" | [runs] | join(", ")</ |
"gHHH5YY++///\\" | [runs] | join(", ")</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
Using the -r ("raw output") command-line option of jq: |
Using the -r ("raw output") command-line option of jq: |
||
<pre>g, HHH, 5, YY, ++, ///, \</pre> |
<pre>g, HHH, 5, YY, ++, ///, \</pre> |
||
=={{header|Jsish}}== |
|||
Showing off a little unit testing... |
|||
Starting with |
|||
<syntaxhighlight lang="javascript">#!/usr/bin/env jsish |
|||
;'Split a string based on change of character, in Jsish'; |
|||
function splitOnChange(str:string):string { |
|||
if (str.length < 2) return str; |
|||
var last = str[0]; |
|||
var result = last; |
|||
for (var pos = 1; pos < str.length; pos++) { |
|||
result += ((last == str[pos]) ? last : ', ' + str[pos]); |
|||
last = str[pos]; |
|||
} |
|||
return result; |
|||
} |
|||
provide('splitOnChange', 1.0); |
|||
/* literal backslash needs escaping during initial processing */ |
|||
;splitOnChange('gHHH5YY++///\\'); |
|||
;splitOnChange('a'); |
|||
;splitOnChange('ab'); |
|||
;splitOnChange('aaa'); |
|||
;splitOnChange('aaaba'); |
|||
;splitOnChange('gH HH5YY++//,/\\');</syntaxhighlight> |
|||
Then |
|||
<pre> |
|||
prompt$ jsish -u -update true splitOnChange.jsi |
|||
Created splitOnChange.jsi</pre> |
|||
Giving |
|||
<syntaxhighlight lang="javascript">#!/usr/bin/env jsish |
|||
;'Split a string based on change of character, in Jsish'; |
|||
function splitOnChange(str:string):string { |
|||
if (str.length < 2) return str; |
|||
var last = str[0]; |
|||
var result = last; |
|||
for (var pos = 1; pos < str.length; pos++) { |
|||
(last == str[pos]) ? result += last : result += ', ' + str[pos]; |
|||
last = str[pos]; |
|||
} |
|||
return result; |
|||
} |
|||
provide('splitOnChange', 1.0); |
|||
/* literal backslash needs escaping during initial processing */ |
|||
;splitOnChange('gHHH5YY++///\\'); |
|||
;splitOnChange('a'); |
|||
;splitOnChange('ab'); |
|||
;splitOnChange('aaa'); |
|||
;splitOnChange('aaaba'); |
|||
;splitOnChange('gH HH5YY++//,/\\'); |
|||
/* |
|||
=!EXPECTSTART!= |
|||
'Split a string based on change of character, in Jsish' |
|||
splitOnChange('gHHH5YY++///\') ==> g, HHH, 5, YY, ++, ///, \ |
|||
splitOnChange('a') ==> a |
|||
splitOnChange('ab') ==> a, b |
|||
splitOnChange('aaa') ==> aaa |
|||
splitOnChange('aaaba') ==> aaa, b, a |
|||
splitOnChange('gH HH5YY++//,/\') ==> g, H, , HH, 5, YY, ++, //, ,, /, \ |
|||
=!EXPECTEND!= |
|||
*/</syntaxhighlight> |
|||
Which tests as: |
|||
<pre>prompt$ jsish -u splitOnChange.jsi |
|||
[PASS] splitOnChange.jsi</pre> |
|||
And then satisfying the task of showing the one result, using the script as a module: |
|||
{{out}} |
|||
<pre>prompt$ jsish |
|||
Jsish interactive: see 'help [cmd]'. \ cancels > input. ctrl-c aborts running script. |
|||
# require('splitOnChange'); |
|||
1 |
|||
# puts(splitOnChange('gHHH5YY++///\\')); |
|||
g, HHH, 5, YY, ++, ///, \</pre> |
|||
=={{header|Julia}}== |
=={{header|Julia}}== |
||
< |
<syntaxhighlight lang="julia"># v0.6 |
||
using IterTools |
using IterTools |
||
str = "gHHH5YY++///\\" |
str = "gHHH5YY++///\\" |
||
sep = map(join, groupby(identity, str)) |
sep = map(join, groupby(identity, str)) |
||
println("string: $str\nseparated: ", join(sep, ", "))</ |
println("string: $str\nseparated: ", join(sep, ", "))</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre>string: gHHH5YY++///\ |
<pre>string: gHHH5YY++///\ |
||
separated: g, HHH, 5, YY, ++, ///, \</pre> |
separated: g, HHH, 5, YY, ++, ///, \</pre> |
||
=={{header|K}}== |
|||
<syntaxhighlight lang="k">split: {(&~=':x)_x} |
|||
","/ split "gHHH5YY++///\\"</syntaxhighlight> |
|||
{{out}} |
|||
<pre>"g,HHH,5,YY,++,///,\\"</pre> |
|||
=={{header|Kotlin}}== |
=={{header|Kotlin}}== |
||
< |
<syntaxhighlight lang="kotlin">// version 1.0.6 |
||
fun splitOnChange(s: String): String { |
fun splitOnChange(s: String): String { |
||
Line 844: | Line 1,825: | ||
val s = """gHHH5YY++///\""" |
val s = """gHHH5YY++///\""" |
||
println(splitOnChange(s)) |
println(splitOnChange(s)) |
||
}</ |
}</syntaxhighlight> |
||
{{out}} |
|||
<pre> |
|||
g, HHH, 5, YY, ++, ///, \ |
|||
</pre> |
|||
=== Using fold() === |
|||
<syntaxhighlight lang="kotlin"> |
|||
fun splitOnChange(src: String): String = |
|||
src.fold("") { acc, c -> |
|||
if (acc.isEmpty() || acc.last() == c) "$acc$c" else "$acc, $c" |
|||
} |
|||
fun main() { |
|||
splitOnChange("""gHHH5YY++///\""").also { println(it)} |
|||
} |
|||
</syntaxhighlight> |
|||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
||
g, HHH, 5, YY, ++, ///, \ |
g, HHH, 5, YY, ++, ///, \ |
||
</pre> |
</pre> |
||
=={{header|Lambdatalk}}== |
|||
<syntaxhighlight lang="scheme"> |
|||
{def mysplit |
|||
{def mysplit.r |
|||
{lambda {:w :i} |
|||
{if {> :i {W.length :w}} |
|||
then |
|||
else {if {not {W.equal? {W.get :i :w} {W.get {+ :i 1} :w}}} |
|||
then ____ else} {W.get {+ :i 1} :w}{mysplit.r :w {+ :i 1}}}}} |
|||
{lambda {:w} |
|||
{S.replace ____ by in {mysplit.r #:w 0}}}} |
|||
-> mysplit |
|||
{mysplit gHHH5YY++///\} |
|||
-> g HHH 5 YY ++ /// \ |
|||
</syntaxhighlight> |
|||
=={{header|Lua}}== |
=={{header|Lua}}== |
||
Note that the backslash must be quoted as a double backslash as Lua uses C-like escape sequences. |
Note that the backslash must be quoted as a double backslash as Lua uses C-like escape sequences. |
||
< |
<syntaxhighlight lang="lua">function charSplit (inStr) |
||
local outStr, nextChar = inStr:sub(1, 1) |
local outStr, nextChar = inStr:sub(1, 1) |
||
for pos = 2, #inStr do |
for pos = 2, #inStr do |
||
Line 865: | Line 1,879: | ||
end |
end |
||
print(charSplit("gHHH5YY++///\\"))</ |
print(charSplit("gHHH5YY++///\\"))</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre>g, HHH, 5, YY, ++, ///, \</pre> |
<pre>g, HHH, 5, YY, ++, ///, \</pre> |
||
Line 871: | Line 1,885: | ||
'''Alternative:''' |
'''Alternative:''' |
||
Simply scan difference in reverse order and insert delimiter in place, the loop counter i will not update with length of s. |
Simply scan difference in reverse order and insert delimiter in place, the loop counter i will not update with length of s. |
||
< |
<syntaxhighlight lang="lua">function splitdiff(s) |
||
for i=#s,2,-1 do |
for i=#s,2,-1 do |
||
if s:sub(i,i)~=s:sub(i-1,i-1) then |
if s:sub(i,i)~=s:sub(i-1,i-1) then |
||
Line 878: | Line 1,892: | ||
end |
end |
||
return s |
return s |
||
end</ |
end</syntaxhighlight> |
||
=={{header|Ksh}}== |
|||
<syntaxhighlight lang="ksh"> |
|||
#!/bin/ksh |
|||
# Split a character string based on change of character |
|||
# # Variables: |
|||
# |
|||
str='gHHH5YY++///\' |
|||
delim=', ' |
|||
# # Functions: |
|||
# |
|||
# # Function _splitonchg(str, delim) - return str split by delim at char change |
|||
# |
|||
function _splitonchg { |
|||
typeset _str ; _str="$1" |
|||
typeset _delim ; _delim="$2" |
|||
typeset _i _splitstr ; integer _i |
|||
for ((_i=1; _i<${#_str}+1; _i++)); do |
|||
if [[ "${_str:$((_i-1)):1}" != "${_str:${_i}:1}" ]]; then |
|||
_splitstr+="${_str:$((_i-1)):1}${_delim}" |
|||
else |
|||
_splitstr+="${_str:$((_i-1)):1}" |
|||
fi |
|||
done |
|||
echo "${_splitstr%"${_delim}"*}" |
|||
} |
|||
###### |
|||
# main # |
|||
###### |
|||
print "Original: ${str}" |
|||
print " Split: $(_splitonchg "${str}" "${delim}")" |
|||
</syntaxhighlight> |
|||
{{out}}<pre> |
|||
Original: gHHH5YY++///\ |
|||
Split: g, HHH, 5, YY, ++, ///, \</pre> |
|||
=={{header|M2000 Interpreter}}== |
=={{header|M2000 Interpreter}}== |
||
Stack New open a new stack object as current stack, and keep the old one. After the end of block execution old stack get back as current stack. Data statement push to bottom (we read from top, so using data we get a FIFO type). Letter$ pops a string or raise an error if no string found at the top of stack. |
Stack New open a new stack object as current stack, and keep the old one. After the end of block execution old stack get back as current stack. Data statement push to bottom (we read from top, so using data we get a FIFO type). Letter$ pops a string or raise an error if no string found at the top of stack. |
||
<syntaxhighlight lang="m2000 interpreter"> |
|||
<lang M2000 Interpreter> |
|||
Module PrintParts(splitthis$) { |
Module PrintParts(splitthis$) { |
||
Def string m$, p$ |
Def string m$, p$ |
||
Line 905: | Line 1,960: | ||
} |
} |
||
PrintParts "gHHH5YY++///\" |
PrintParts "gHHH5YY++///\" |
||
</syntaxhighlight> |
|||
</lang> |
|||
=={{header|Maple}}== |
=={{header|Maple}}== |
||
Added an additional backlash to escape the \ character at the end. |
Added an additional backlash to escape the \ character at the end. |
||
< |
<syntaxhighlight lang="maple">splitChange := proc(str::string) |
||
local start,i,len; |
local start,i,len; |
||
start := 1; |
start := 1; |
||
Line 922: | Line 1,976: | ||
printf("%s", str[start..len]); |
printf("%s", str[start..len]); |
||
end proc; |
end proc; |
||
splitChange("gHHH5YY++///\\");</ |
splitChange("gHHH5YY++///\\");</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre>g, HHH, 5, YY, ++, ///, \</pre> |
<pre>g, HHH, 5, YY, ++, ///, \</pre> |
||
=={{header|Mathematica}}== |
=={{header|Mathematica}}/{{header|Wolfram Language}}== |
||
The backslash (\) must be escaped with another backslash when defining the string. |
The backslash (\) must be escaped with another backslash when defining the string. |
||
< |
<syntaxhighlight lang="mathematica">StringJoin@@Riffle[StringCases["gHHH5YY++///\\", p : (x_) .. -> p], ", "]</syntaxhighlight> |
||
{{out}} |
|||
<pre>g, HHH, 5, YY, ++, ///, \</pre> |
|||
=={{header|MiniScript}}== |
|||
<syntaxhighlight lang="miniscript">s = "gHHH5YY++///\" |
|||
output = [] |
|||
lastLetter = s[0] |
|||
for letter in s |
|||
if letter != lastLetter then output.push ", " |
|||
output.push letter |
|||
lastLetter = letter |
|||
end for |
|||
print output.join("")</syntaxhighlight> |
|||
{{out}} |
{{out}} |
||
<pre>g, HHH, 5, YY, ++, ///, \ |
<pre>g, HHH, 5, YY, ++, ///, \</pre> |
||
</pre> |
|||
=={{header|Modula-2}}== |
=={{header|Modula-2}}== |
||
< |
<syntaxhighlight lang="modula2">MODULE CharacterChange; |
||
FROM Terminal IMPORT Write,WriteString,WriteLn,ReadChar; |
FROM Terminal IMPORT Write,WriteString,WriteLn,ReadChar; |
||
Line 960: | Line 2,024: | ||
ReadChar |
ReadChar |
||
END CharacterChange.</ |
END CharacterChange.</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre>g |
<pre>g |
||
Line 971: | Line 2,035: | ||
=={{header|Nim}}== |
=={{header|Nim}}== |
||
<syntaxhighlight lang="nim">proc splitOnDiff(str: string): string = |
|||
{{output?}} |
|||
<lang nim> proc splitOnDiff(str: string) : string = |
|||
result = "" |
result = "" |
||
if str.len < 1: return result |
if str.len < 1: return result |
||
var prevChar |
var prevChar: char = str[0] |
||
for idx in 0 .. |
for idx in 0 ..< str.len: |
||
if str[idx] != prevChar: |
if str[idx] != prevChar: |
||
result &= ", " |
result &= ", " |
||
Line 987: | Line 2,048: | ||
result &= str[idx] |
result &= str[idx] |
||
assert splitOnDiff("""X""") == """X""" |
assert splitOnDiff("""X""") == """X""" |
||
Line 994: | Line 2,054: | ||
assert splitOnDiff("""gHHH5YY++///\""") == """g, HHH, 5, YY, ++, ///, \""" |
assert splitOnDiff("""gHHH5YY++///\""") == """g, HHH, 5, YY, ++, ///, \""" |
||
echo splitOnDiff("""gHHH5YY++///\""")</ |
echo splitOnDiff("""gHHH5YY++///\""")</syntaxhighlight> |
||
{{output}} |
|||
<pre>g, HHH, 5, YY, ++, ///, \</pre> |
|||
=={{header|ooRexx}}== |
=={{header|ooRexx}}== |
||
< |
<syntaxhighlight lang="oorexx">Parse Arg str . /*obtain optional arguments from the CL*/ |
||
If str=='' Then str= 'gHHH5YY++///\' /*Not specified? Then use the default.*/ |
If str=='' Then str= 'gHHH5YY++///\' /*Not specified? Then use the default.*/ |
||
i=1 |
i=1 |
||
Line 1,010: | Line 2,072: | ||
i=j |
i=j |
||
End |
End |
||
Say ol</ |
Say ol</syntaxhighlight> |
||
{{out}} |
|||
<pre>g, HHH, 5, YY, ++, ///, \</pre> |
|||
=={{header|Pascal}}== |
|||
<syntaxhighlight lang="pascal">program SplitChars; |
|||
{$IFDEF FPC} |
|||
{$MODE DELPHI}{$COPERATORS ON} |
|||
{$ENDIF} |
|||
const |
|||
TestString = 'gHHH5YY++///\'; |
|||
function SplitAtChars(const S: String):String; |
|||
var |
|||
i : integer; |
|||
lastChar:Char; |
|||
begin |
|||
result := ''; |
|||
IF length(s) > 0 then |
|||
begin |
|||
LastChar := s[1]; |
|||
result := LastChar; |
|||
For i := 2 to length(s) do |
|||
begin |
|||
if s[i] <> lastChar then |
|||
begin |
|||
lastChar := s[i]; |
|||
result += ', '; |
|||
end; |
|||
result += LastChar; |
|||
end; |
|||
end; |
|||
end; |
|||
BEGIN |
|||
writeln(SplitAtChars(TestString)); |
|||
end.</syntaxhighlight> |
|||
{{out}} |
{{out}} |
||
<pre>g, HHH, 5, YY, ++, ///, \</pre> |
<pre>g, HHH, 5, YY, ++, ///, \</pre> |
||
=={{header|Perl}}== |
=={{header|Perl}}== |
||
<syntaxhighlight lang="perl">use strict; |
|||
{{works with|Perl|5.x}} |
|||
use warnings; |
|||
<lang perl>my $str = 'gHHH5YY++///\\'; |
|||
use feature 'say'; |
|||
$str =~ s/((.)\g{-1}*)/$1, /g; |
|||
use utf8; |
|||
$str =~ s/, $//; # remove trailing , |
|||
binmode(STDOUT, ':utf8'); |
|||
print "$str\n"; |
|||
</lang> |
|||
for my $string (q[gHHH5YY++///\\], q[fffn⃗n⃗n⃗»»» ℵℵ☄☄☃☃̂☃🤔🇺🇸🤦♂️👨👩👧👦]) { |
|||
my @S; |
|||
my $last = ''; |
|||
while ($string =~ /(\X)/g) { |
|||
if ($last eq $1) { $S[-1] .= $1 } else { push @S, $1 } |
|||
$last = $1; |
|||
} |
|||
say "Orginal: $string\n Split: 「" . join('」, 「', @S) . "」\n"; |
|||
}</syntaxhighlight> |
|||
{{out}} |
{{out}} |
||
<pre>Orginal: gHHH5YY++///\ |
|||
Split: 「g」, 「HHH」, 「5」, 「YY」, 「++」, 「///」, 「\」 |
|||
Orginal: fffn⃗n⃗n⃗»»» ℵℵ☄☄☃☃̂☃🤔🇺🇸🤦♂️👨👩👧👦 |
|||
Split: 「fff」, 「」, 「n⃗n⃗n⃗」, 「»»»」, 「 」, 「ℵℵ」, 「☄☄」, 「☃」, 「☃̂」, 「☃」, 「🤔」, 「🇺🇸」, 「🤦♂️」, 「👨👩👧👦」</pre> |
|||
=={{header|Phix}}== |
|||
<!--<syntaxhighlight lang="phix">--> |
|||
<span style="color: #008080;">function</span> <span style="color: #000000;">split_on_change</span><span style="color: #0000FF;">(</span><span style="color: #004080;">string</span> <span style="color: #000000;">s</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #004080;">string</span> <span style="color: #000000;">res</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">""</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">s</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span> |
|||
<span style="color: #004080;">integer</span> <span style="color: #000000;">prev</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">s</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]</span> |
|||
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">s</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span> |
|||
<span style="color: #004080;">integer</span> <span style="color: #000000;">ch</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">s</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #000000;">ch</span><span style="color: #0000FF;">!=</span><span style="color: #000000;">prev</span> <span style="color: #008080;">then</span> |
|||
<span style="color: #000000;">res</span> <span style="color: #0000FF;">&=</span> <span style="color: #008000;">", "</span> |
|||
<span style="color: #000000;">prev</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">ch</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #000000;">res</span> <span style="color: #0000FF;">&=</span> <span style="color: #000000;">ch</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #008080;">return</span> <span style="color: #000000;">res</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span> |
|||
<span style="color: #7060A8;">puts</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">split_on_change</span><span style="color: #0000FF;">(</span><span style="color: #008000;">`gHHH5YY++///\`</span><span style="color: #0000FF;">))</span> |
|||
<!--</syntaxhighlight>--> |
|||
{{Out}} |
|||
<pre> |
<pre> |
||
g, HHH, 5, YY, ++, ///, \ |
g, HHH, 5, YY, ++, ///, \ |
||
</pre> |
</pre> |
||
=={{header| |
=={{header|Phixmonti}}== |
||
<syntaxhighlight lang="Phixmonti">/# Rosetta Code problem: https://rosettacode.org/wiki/Split_a_character_string_based_on_change_of_character |
|||
{{works with|Rakudo|2017.05}} |
|||
by Galileo, 11/2022 #/ |
|||
include ..\Utilitys.pmt |
|||
<lang perl6>sub group-chars ($str) { $str.comb: / (.) $0* / } |
|||
"" |
|||
# Testing: |
|||
"gHHH5YY++///\" 1 get >ps |
|||
len for get |
|||
for Q[gHHH5YY++///\], Q[fffn⃗n⃗n⃗»»» ℵℵ☄☄☃☃̂☃🤔🇺🇸🤦♂️👨👩👧👦] -> $string { |
|||
dup tps == if |
|||
rot swap chain swap |
|||
put ' Split: ', group-chars($string).join(', '); |
|||
else |
|||
}</lang> |
|||
ps> drop >ps |
|||
swap ", " tps chain chain swap |
|||
endif |
|||
endfor |
|||
pstack</syntaxhighlight> |
|||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
||
["g, HHH, 5, YY, ++, ///, \", "gHHH5YY++///\"] |
|||
Split: g, HHH, 5, YY, ++, ///, \ |
|||
Original: fffn⃗n⃗n⃗»»» ℵℵ☄☄☃☃̂☃🤔🇺🇸🤦♂️👨👩👧👦 |
|||
Split: fff, , n⃗n⃗n⃗, »»», , ℵℵ, ☄☄, ☃, ☃̂, ☃, 🤔, 🇺🇸, 🤦♂️, 👨👩👧👦 |
|||
</pre> |
|||
=== Press any key to exit ===</pre> |
|||
The second test-case is to show that Perl 6 works with strings on the Unicode grapheme level, handles whitespace, combiners, and zero width characters up to Unicode Version 9.0, and multi-byte Emoji characters up to Version 4.0 correctly. (Perl 6 provisionally handles Unicode Versions 10.0 and Emoji Version 5.0 but they aren't released yet so aren't officially supported.) For those of you with browsers unable to display the second string, it consists of: |
|||
* {LATIN SMALL LETTER F} x 3 |
|||
* {ZERO WIDTH NO-BREAK SPACE} x 3 |
|||
* {LATIN SMALL LETTER N, COMBINING RIGHT ARROW ABOVE} x 3 |
|||
* {RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK} x 3 |
|||
* {SPACE} x 2, |
|||
* {ALEF SYMBOL} x 2, |
|||
* {COMET} x 2, |
|||
* {SNOWMAN} x 1, |
|||
* {SNOWMAN, COMBINING CIRCUMFLEX ACCENT} x 1 |
|||
* {SNOWMAN} x 1, |
|||
* {THINKING FACE} x 1 |
|||
* {REGIONAL INDICATOR SYMBOL LETTER U, REGIONAL INDICATOR SYMBOL LETTER S} x 1 |
|||
* {FACE PALM, ZERO WIDTH JOINER, MALE SIGN, VARIATION SELECTOR-16} x 1 |
|||
* {MAN, ZERO WIDTH JOINER, WOMAN, ZERO WIDTH JOINER, GIRL, ZERO WIDTH JOINER, BOY} x 1 |
|||
=={{header| |
=={{header|PicoLisp}}== |
||
<syntaxhighlight lang="picolisp">(de splitme (Str) |
|||
<lang Phix>function split_on_change(string in) |
|||
(let (Str (chop Str) Fin) |
|||
string out = "" |
|||
(glue |
|||
", " |
|||
(make |
|||
(for X Str |
|||
if |
(if (= X (car Fin)) |
||
(conc Fin (cons X)) |
|||
(link (setq Fin (cons X))) ) ) ) ) ) ) |
|||
(prinl (splitme "gHHH5YY++///\\"))</syntaxhighlight> |
|||
end if |
|||
{{out}} |
|||
out &= ch |
|||
<pre>g, HHH, 5, YY, ++, ///, \</pre> |
|||
end for |
|||
end if |
|||
return out |
|||
end function |
|||
=={{header|Pike}}== |
|||
puts(1,split_on_change(`gHHH5YY++///\`))</lang> |
|||
<syntaxhighlight lang="pike"> |
|||
string input = "gHHH5YY++///\\"; // \ needs escaping |
|||
string last_char; |
|||
foreach(input/1, string char) { |
|||
if(last_char && char != last_char) |
|||
write(", "); |
|||
write(char); |
|||
last_char = char; |
|||
} |
|||
</syntaxhighlight> |
|||
{{Out}} |
{{Out}} |
||
<pre> |
|||
g, HHH, 5, YY, ++, ///, \ |
|||
</pre> |
|||
=={{header|Plain English}}== |
|||
To make sense of this example, you must understand riders. A rider is a simple abstraction for efficiently parsing strings. A rider is a record with an original substring, a source substring, and a token substring. |
|||
After executing the following code, for example: |
|||
<syntaxhighlight lang="plainenglish">Put "abcdef" into a string. |
|||
Slap a rider on the string.</syntaxhighlight> |
|||
The rider looks like this: |
|||
<syntaxhighlight lang="plainenglish">Original: "abcdef" |
|||
Source: "abcdef" |
|||
Token: ""</syntaxhighlight> |
|||
Now when we <code>Bump the rider.</code>, it looks like this: |
|||
<syntaxhighlight lang="plainenglish">Original: "abcdef" |
|||
Source: "bcdef" |
|||
Token: "a"</syntaxhighlight> |
|||
Another bump, and: |
|||
<syntaxhighlight lang="plainenglish">Original: "abcdef" |
|||
Source: "cdef" |
|||
Token: "ab"</syntaxhighlight> |
|||
Now let's say we have a complete token and want to start a new one. We can |
|||
<code>Position the rider's token on the rider's source.</code> |
|||
and now the rider looks like this: |
|||
<syntaxhighlight lang="plainenglish">Original: "abcdef" |
|||
Source: "cdef" |
|||
Token: ""</syntaxhighlight> |
|||
And that's all there is to it. |
|||
<syntaxhighlight lang="plainenglish">To run: |
|||
Start up. |
|||
Split "gHHH5YY++///\" into some string things by change of character. |
|||
Write the string things on the console. |
|||
Destroy the string things. |
|||
Wait for the escape key. |
|||
Shut down. |
|||
To split a string into some string things by change of character: |
|||
If the string's length is less than 2, add the string to the string things; exit. |
|||
Slap a rider on the string. |
|||
Loop. |
|||
Move the rider (change of character rules). |
|||
Add the rider's token to the string things. |
|||
If the rider's source is blank, exit. |
|||
Repeat. |
|||
To move a rider (change of character rules): |
|||
Position the rider's token on the rider's source. |
|||
Loop. |
|||
If the rider's source is blank, exit. |
|||
If the rider's token is blank, bump the rider; repeat. |
|||
Put the rider's token's last plus 1 into a byte pointer. |
|||
If the rider's token's last's target is not the byte pointer's target, exit. |
|||
Bump the rider. |
|||
Repeat. |
|||
To write some string things to a console; |
|||
To write some string things on a console: |
|||
Get a string thing from the string things. |
|||
Loop. |
|||
If the string thing is nil, write "" on the console; exit. |
|||
Write the string thing's string on the console without advancing. |
|||
If the string thing's next is not nil, write ", " on the console without advancing. |
|||
Put the string thing's next into the string thing. |
|||
Repeat.</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
<pre> |
||
g, HHH, 5, YY, ++, ///, \ |
g, HHH, 5, YY, ++, ///, \ |
||
Line 1,087: | Line 2,290: | ||
=={{header|PowerShell}}== |
=={{header|PowerShell}}== |
||
{{trans|BBC BASIC}} |
{{trans|BBC BASIC}} |
||
<syntaxhighlight lang="powershell"> |
|||
<lang PowerShell> |
|||
function Split-String ([string]$String) |
function Split-String ([string]$String) |
||
{ |
{ |
||
Line 1,108: | Line 2,311: | ||
$splitString |
$splitString |
||
} |
} |
||
</syntaxhighlight> |
|||
</lang> |
|||
<syntaxhighlight lang="powershell"> |
|||
<lang PowerShell> |
|||
Split-String "gHHH5YY++///\" |
Split-String "gHHH5YY++///\" |
||
</syntaxhighlight> |
|||
</lang> |
|||
{{Out}} |
{{Out}} |
||
<pre> |
<pre> |
||
Line 1,118: | Line 2,321: | ||
=={{header|PureBasic}}== |
=={{header|PureBasic}}== |
||
< |
<syntaxhighlight lang="purebasic">Procedure splitstring(s$) |
||
Define *p.Character = @s$, |
Define *p.Character = @s$, |
||
c_buf.c = *p\c |
c_buf.c = *p\c |
||
Line 1,136: | Line 2,339: | ||
splitstring("gHHH5YY++///\") |
splitstring("gHHH5YY++///\") |
||
Input() |
Input() |
||
EndIf</ |
EndIf</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre>g, HHH, 5, YY, ++, ///, \</pre> |
<pre>g, HHH, 5, YY, ++, ///, \</pre> |
||
Line 1,144: | Line 2,347: | ||
===Python3.6+=== |
===Python3.6+=== |
||
Using [[https://docs.python.org/3.6/library/itertools.html#itertools.groupby itertools.groupby]]. |
Using [[https://docs.python.org/3.6/library/itertools.html#itertools.groupby itertools.groupby]]. |
||
< |
<syntaxhighlight lang="python">from itertools import groupby |
||
def splitter(text): |
def splitter(text): |
||
Line 1,151: | Line 2,354: | ||
if __name__ == '__main__': |
if __name__ == '__main__': |
||
txt = 'gHHH5YY++///\\' # Note backslash is the Python escape char. |
txt = 'gHHH5YY++///\\' # Note backslash is the Python escape char. |
||
print(f'Input: {txt}\nSplit: {splitter(txt)}')</ |
print(f'Input: {txt}\nSplit: {splitter(txt)}')</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre>Input: gHHH5YY++///\ |
<pre>Input: gHHH5YY++///\ |
||
Split: g, HHH, 5, YY, ++, ///, \</pre> |
Split: g, HHH, 5, YY, ++, ///, \</pre> |
||
===Python: Using zip=== |
|||
<syntaxhighlight lang="python">def splitterz(text): |
|||
return (''.join(x + ('' if x == nxt else ', ') |
|||
for x, nxt in zip(txt, txt[1:] + txt[-1]))) |
|||
if __name__ == '__main__': |
|||
txt = 'gHHH5YY++///\\' |
|||
print(splitterz(txt))</syntaxhighlight> |
|||
{{out}} |
|||
<pre>g, HHH, 5, YY, ++, ///, \</pre> |
|||
===Python2=== |
===Python2=== |
||
< |
<syntaxhighlight lang="python">import itertools |
||
try: input = raw_input |
try: input = raw_input |
||
Line 1,168: | Line 2,383: | ||
groups.append(''.join(g)) |
groups.append(''.join(g)) |
||
print(' input string: %s' % s) |
print(' input string: %s' % s) |
||
print(' output string: %s' % ', '.join(groups))</ |
print(' output string: %s' % ', '.join(groups))</syntaxhighlight> |
||
{{out}} when using the default input: |
{{out}} when using the default input: |
||
<pre> |
<pre> |
||
input string: gHHH5YY++///\ |
input string: gHHH5YY++///\ |
||
output string: g, HHH, 5, YY, ++, ///, \ |
output string: g, HHH, 5, YY, ++, ///, \ |
||
</pre> |
|||
=={{header|Quackery}}== |
|||
<syntaxhighlight lang="quackery">[ dup size 2 < |
|||
iff size done |
|||
behead swap |
|||
[] nested join |
|||
witheach |
|||
[ over != if |
|||
[ drop i^ 1+ |
|||
conclude ] ] ] is $run ( $ --> n ) |
|||
[ dup size 2 < if done |
|||
dup $run split |
|||
dup [] = |
|||
iff drop done |
|||
dip [ $ ", " join ] |
|||
recurse join ] is runs$ ( $ --> $ ) |
|||
</syntaxhighlight> |
|||
'''Testing in Quackery shell.''' |
|||
<pre>/O> $ "gHHH5YY++///\" runs$ echo$ |
|||
... |
|||
g, HHH, 5, YY, ++, ///, \ |
|||
Stack empty. |
|||
</pre> |
</pre> |
||
=={{header|Racket}}== |
=={{header|Racket}}== |
||
{{trans|Python}} |
{{trans|Python}} |
||
< |
<syntaxhighlight lang="racket">#lang racket |
||
(define (split-strings-on-change s) |
(define (split-strings-on-change s) |
||
(map list->string (group-by values (string->list s) char=?))) |
(map list->string (group-by values (string->list s) char=?))) |
||
Line 1,185: | Line 2,424: | ||
< |
< |
||
) |
) |
||
", "))</ |
", "))</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre>g, HHH, 5, YY, ++, ///, \</pre> |
<pre>g, HHH, 5, YY, ++, ///, \</pre> |
||
=={{header|Raku}}== |
|||
(formerly Perl 6) |
|||
{{works with|Rakudo|2017.05}} |
|||
<syntaxhighlight lang="raku" line>sub group-chars ($str) { $str.comb: / (.) $0* / } |
|||
# Testing: |
|||
for Q[gHHH5YY++///\], Q[fffn⃗n⃗n⃗»»» ℵℵ☄☄☃☃̂☃🤔🇺🇸🤦♂️👨👩👧👦] -> $string { |
|||
put 'Original: ', $string; |
|||
put ' Split: ', group-chars($string).join(', '); |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
Original: gHHH5YY++///\ |
|||
Split: g, HHH, 5, YY, ++, ///, \ |
|||
Original: fffn⃗n⃗n⃗»»» ℵℵ☄☄☃☃̂☃🤔🇺🇸🤦♂️👨👩👧👦 |
|||
Split: fff, , n⃗n⃗n⃗, »»», , ℵℵ, ☄☄, ☃, ☃̂, ☃, 🤔, 🇺🇸, 🤦♂️, 👨👩👧👦 |
|||
</pre> |
|||
The second test-case is to show that Raku works with strings on the Unicode grapheme level, handles whitespace, combiners, and zero width characters up to Unicode Version 13.0 correctly. (Raku generally tracks updates to the Unicode spec and typically lags no more than a month behind.) For those of you with browsers unable to display the second string, it consists of: |
|||
* {LATIN SMALL LETTER F} x 3 |
|||
* {ZERO WIDTH NO-BREAK SPACE} x 3 |
|||
* {LATIN SMALL LETTER N, COMBINING RIGHT ARROW ABOVE} x 3 |
|||
* {RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK} x 3 |
|||
* {SPACE} x 2, |
|||
* {ALEF SYMBOL} x 2, |
|||
* {COMET} x 2, |
|||
* {SNOWMAN} x 1, |
|||
* {SNOWMAN, COMBINING CIRCUMFLEX ACCENT} x 1 |
|||
* {SNOWMAN} x 1, |
|||
* {THINKING FACE} x 1 |
|||
* {REGIONAL INDICATOR SYMBOL LETTER U, REGIONAL INDICATOR SYMBOL LETTER S} x 1 |
|||
* {FACE PALM, ZERO WIDTH JOINER, MALE SIGN, VARIATION SELECTOR-16} x 1 |
|||
* {MAN, ZERO WIDTH JOINER, WOMAN, ZERO WIDTH JOINER, GIRL, ZERO WIDTH JOINER, BOY} x 1 |
|||
=={{header|Refal}}== |
|||
<syntaxhighlight lang="refal">$ENTRY Go { |
|||
= <Prout <Join (', ') <Split 'gHHH5YY++///\\'>>>; |
|||
}; |
|||
Split { |
|||
(e.Cur) = (e.Cur); |
|||
(e.Cur s.1) s.1 e.X = <Split (e.Cur s.1 s.1) e.X>; |
|||
(e.Cur) s.1 e.X = (e.Cur) <Split (s.1) e.X>; |
|||
s.1 e.X = <Split (s.1) e.X>; |
|||
}; |
|||
Join { |
|||
(e.Joiner) = ; |
|||
(e.Joiner) (e.Str) = e.Str; |
|||
(e.Joiner) (e.Str) e.Strs = e.Str e.Joiner <Join (e.Joiner) e.Strs>; |
|||
};</syntaxhighlight> |
|||
{{out}} |
|||
<pre>g, HHH, 5, YY, ++, ///, \</pre> |
|||
=={{header|REXX}}== |
=={{header|REXX}}== |
||
===version 1=== |
===version 1=== |
||
< |
<syntaxhighlight lang="rexx">/*REXX program splits a string based on change of character ───► a comma delimited list.*/ |
||
parse arg str /*obtain optional arguments from the CL*/ |
parse arg str /*obtain optional arguments from the CL*/ |
||
if str=='' then str= 'gHHH5YY++///\' /*Not specified? Then use the default.*/ |
if str=='' then str= 'gHHH5YY++///\' /*Not specified? Then use the default.*/ |
||
Line 1,202: | Line 2,497: | ||
end /*j*/ /* [↓] keep peeling chars until done. */ |
end /*j*/ /* [↓] keep peeling chars until done. */ |
||
say ' input string: ' str /*display the original string & output.*/ |
say ' input string: ' str /*display the original string & output.*/ |
||
say ' output string: ' $ /*stick a fork in it, we're all done. */</ |
say ' output string: ' $ /*stick a fork in it, we're all done. */</syntaxhighlight> |
||
{{out|output|text= when using the default input:}} |
{{out|output|text= when using the default input:}} |
||
<pre> |
<pre> |
||
Line 1,210: | Line 2,505: | ||
===version 2=== |
===version 2=== |
||
< |
<syntaxhighlight lang="rexx">/* REXX */ |
||
Parse arg str /*obtain optional arguments from the CL*/ |
Parse arg str /*obtain optional arguments from the CL*/ |
||
if str=='' then str= 'gHHH5YY++///\' /*Not specified? Then use the default.*/ |
if str=='' then str= 'gHHH5YY++///\' /*Not specified? Then use the default.*/ |
||
Line 1,229: | Line 2,524: | ||
result=result||x |
result=result||x |
||
say ' input string: ' input |
say ' input string: ' input |
||
say ' output string: ' result </ |
say ' output string: ' result </syntaxhighlight> |
||
{{out]] |
{{out]] |
||
<pre> input string: gHHH5YY++///\ |
<pre> input string: gHHH5YY++///\ |
||
Line 1,235: | Line 2,530: | ||
=={{header|Ring}}== |
=={{header|Ring}}== |
||
< |
<syntaxhighlight lang="ring"> |
||
see split("gHHH5YY++///\") |
see split("gHHH5YY++///\") |
||
Line 1,250: | Line 2,545: | ||
next |
next |
||
return split |
return split |
||
</syntaxhighlight> |
|||
</lang> |
|||
Output: |
Output: |
||
<pre> |
<pre> |
||
g, HHH, 5, YY, ++, ///, \ |
g, HHH, 5, YY, ++, ///, \ |
||
</pre> |
</pre> |
||
=={{header|RPL}}== |
|||
≪ → text |
|||
≪ "" text 1 1 SUB |
|||
1 text SIZE '''FOR''' j |
|||
text j DUP SUB |
|||
'''IF''' DUP2 ≠ '''THEN''' SWAP DROP ", " OVER + '''END''' |
|||
ROT SWAP + SWAP |
|||
'''NEXT '''DROP |
|||
≫ ≫ ‘<span style="color:blue">COMASPLT</span>’ STO |
|||
=={{header|Ruby}}== |
=={{header|Ruby}}== |
||
< |
<syntaxhighlight lang="ruby">def split(str) |
||
puts " input string: #{str}" |
puts " input string: #{str}" |
||
s = str.chars.chunk(&:itself).map{|_,a| a.join}.join(", ") |
s = str.chars.chunk(&:itself).map{|_,a| a.join}.join(", ") |
||
Line 1,264: | Line 2,569: | ||
end |
end |
||
split("gHHH5YY++///\\")</ |
split("gHHH5YY++///\\")</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
Line 1,273: | Line 2,578: | ||
=={{header|Rust}}== |
=={{header|Rust}}== |
||
< |
<syntaxhighlight lang="rust">fn splitter(string: &str) -> String { |
||
let chars: Vec<_> = string.chars().collect(); |
let chars: Vec<_> = string.chars().collect(); |
||
let mut result = Vec::new(); |
let mut result = Vec::new(); |
||
Line 1,306: | Line 2,611: | ||
println!("input string: {}", test_string); |
println!("input string: {}", test_string); |
||
println!("output string: {}", splitter(test_string)); |
println!("output string: {}", splitter(test_string)); |
||
}</ |
}</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
Line 1,317: | Line 2,622: | ||
output string: g, HHH, 5, YY, ++, ///, \ |
output string: g, HHH, 5, YY, ++, ///, \ |
||
</pre> |
</pre> |
||
===Alternate using IterTools=== |
|||
<syntaxhighlight lang="rust">use itertools::Itertools; |
|||
pub fn split_text(s: &str) -> Vec<String> { |
|||
let mut r = Vec::new(); |
|||
for (_, group) in &s.chars().into_iter().group_by(|e| *e) { |
|||
r.push(group.map(|e| e.to_string()).join("")); |
|||
} |
|||
r |
|||
} |
|||
#[cfg(test)] |
|||
mod tests { |
|||
use super::*; |
|||
#[test] |
|||
fn test_splitting_text() { |
|||
assert_eq!(split_text("gHHH5YY++///\\"), vec!["g", "HHH", "5", "YY", "++", "///", "\\"]); |
|||
assert!(split_text("").is_empty()); |
|||
} |
|||
} |
|||
</syntaxhighlight> |
|||
=={{header|Scala}}== |
=={{header|Scala}}== |
||
< |
<syntaxhighlight lang="scala">// Split a (character) string into comma (plus a blank) delimited strings |
||
// based on a change of character (left to right). |
// based on a change of character (left to right). |
||
// See https://rosettacode.org/wiki/Split_a_character_string_based_on_change_of_character#Scala |
// See https://rosettacode.org/wiki/Split_a_character_string_based_on_change_of_character#Scala |
||
Line 1,326: | Line 2,654: | ||
(s + 'X').sliding(2).map(pair => pair.head + (if (pair.head != pair.last) ", " else "")).mkString("") |
(s + 'X').sliding(2).map(pair => pair.head + (if (pair.head != pair.last) ", " else "")).mkString("") |
||
println(runLengthSplit("""gHHH5YY++///\"""))</ |
println(runLengthSplit("""gHHH5YY++///\"""))</syntaxhighlight> |
||
{{Out}}See it in running in your browser by [https://scalafiddle.io/sf/c4dp8GT/2 ScalaFiddle (JavaScript)] |
{{Out}}See it in running in your browser by [https://scalafiddle.io/sf/c4dp8GT/2 ScalaFiddle (JavaScript)] |
||
or by [https://scastie.scala-lang.org/mDoBS77YSG2Z7w5xdAPzcw Scastie (JVM)]. |
or by [https://scastie.scala-lang.org/mDoBS77YSG2Z7w5xdAPzcw Scastie (JVM)]. |
||
<syntaxhighlight lang="scala"> |
|||
<lang Scala> |
|||
def runLengthSplit(s:String):List[String] = { |
def runLengthSplit(s:String):List[String] = { |
||
def recursiveSplit(acc:List[String], rest:String): List[String] = rest match { |
def recursiveSplit(acc:List[String], rest:String): List[String] = rest match { |
||
Line 1,345: | Line 2,673: | ||
val result = runLengthSplit("""gHHH5YY++///\""") |
val result = runLengthSplit("""gHHH5YY++///\""") |
||
println(result.mkString(",")) |
println(result.mkString(",")) |
||
</syntaxhighlight> |
|||
</lang> |
|||
{{Out}} |
{{Out}} |
||
<pre> |
<pre> |
||
Line 1,352: | Line 2,680: | ||
=={{header|Sed}}== |
=={{header|Sed}}== |
||
<syntaxhighlight lang="sed">echo 'gHHH5YY++///\' | sed 's/\(.\)\1*/&, /g;s/, $//'</syntaxhighlight> |
|||
Output: |
|||
g, HHH, 5, YY, ++, ///, \ |
|||
{{ |
=={{header|SETL}}== |
||
<syntaxhighlight lang="setl">program split_a_character_string_based_on_change_of_character; |
|||
s := "gHHH5YY++///\\"; |
|||
print(join_strings(", ", split_on_change(s))); |
|||
proc split_on_change(s); |
|||
<lang sed> |
|||
parts := []; |
|||
echo 'gHHH5YY++///\' | sed 's/\(.\)\1*/&, /g;s/, $//' |
|||
loop while s /= "" do |
|||
</lang> |
|||
parts with:= span(s, s(1)); |
|||
end loop; |
|||
return parts; |
|||
end proc; |
|||
proc join_strings(s, parts); |
|||
if parts=[] then return ""; end if; |
|||
return parts(1) +/ [s + part : part in parts(2..)]; |
|||
end proc; |
|||
end program;</syntaxhighlight> |
|||
{{out}} |
|||
<pre>g, HHH, 5, YY, ++, ///, \</pre> |
|||
=={{header|Sidef}}== |
=={{header|Sidef}}== |
||
< |
<syntaxhighlight lang="ruby">func group(str) { |
||
gather { |
gather { |
||
while (var match = (str =~ /((.)\g{-1}*)/g)) { |
while (var match = (str =~ /((.)\g{-1}*)/g)) { |
||
Line 1,368: | Line 2,713: | ||
} |
} |
||
say group(ARGV[0] \\ 'gHHH5YY++///\\').join(', ')</ |
say group(ARGV[0] \\ 'gHHH5YY++///\\').join(', ')</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
||
g, HHH, 5, YY, ++, ///, \ |
|||
</pre> |
|||
=={{header|SNOBOL4}}== |
|||
{{works with|SNOBOL4, SPITBOL for Linux}} |
|||
<syntaxhighlight lang="snobol4"> |
|||
* Program: split_on_change_of_character.sbl |
|||
* To run: sbl split_on_change_of_character.sbl |
|||
* Description: Split a (character) string into comma (plus a blank) |
|||
* delimited strings based on a change of character (left to right). |
|||
* |
|||
* Blanks should be treated as any other character |
|||
* (except they are problematic to display clearly). |
|||
* The same applies to commas. |
|||
* |
|||
* For instance, the string: |
|||
* |
|||
* gHHH5YY++///\ |
|||
* should be split and show: |
|||
* |
|||
* g, HHH, 5, YY, ++, ///, \ |
|||
* Comment: Tested using the Spitbol for Linux version of SNOBOL4 |
|||
lf = substr(&alphabet,11,1) ;* New line or line feed |
|||
* Function split_cc will split a string on a change of character. |
|||
define('split_cc(s)tchar,target,post') |
|||
:(split_cc_end) |
|||
split_cc |
|||
tchar = substr(s,1,1) :f(freturn) |
|||
split_cc_pat = span(*tchar) . target (rpos(0) | len(1) . tchar rem) . post |
|||
split_cc2 |
|||
s ? split_cc_pat = post :f(split_cc3) |
|||
split_cc = (ident(split_cc) target, split_cc ', ' target) :s(split_cc2) |
|||
split_cc3 |
|||
:(return) |
|||
split_cc_end |
|||
test_string = "gHHH5YY++///\" |
|||
output = test_string lf |
|||
split_string = split_cc(test_string) |
|||
output = split_string |
|||
END</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
gHHH5YY++///\ |
|||
g, HHH, 5, YY, ++, ///, \ |
g, HHH, 5, YY, ++, ///, \ |
||
</pre> |
</pre> |
||
=={{header|Standard ML}}== |
=={{header|Standard ML}}== |
||
< |
<syntaxhighlight lang="sml">(* |
||
* Head-Tail implementation of grouping |
* Head-Tail implementation of grouping |
||
*) |
*) |
||
Line 1,384: | Line 2,777: | ||
fun group xs = group' nil xs |
fun group xs = group' nil xs |
||
fun groupString str = String.concatWith ", " (map implode (group (explode str)))</ |
fun groupString str = String.concatWith ", " (map implode (group (explode str)))</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre>- groupString "gHHH5YY++///\\"; |
<pre>- groupString "gHHH5YY++///\\"; |
||
val it = "g, HHH, 5, YY, ++, ///, \\" : string</pre> |
val it = "g, HHH, 5, YY, ++, ///, \\" : string</pre> |
||
=={{header|Swift}}== |
|||
<syntaxhighlight lang="swift">public extension String { |
|||
func splitOnChanges() -> [String] { |
|||
guard !isEmpty else { |
|||
return [] |
|||
} |
|||
var res = [String]() |
|||
var workingChar = first! |
|||
var workingStr = "\(workingChar)" |
|||
for char in dropFirst() { |
|||
if char != workingChar { |
|||
res.append(workingStr) |
|||
workingStr = "\(char)" |
|||
workingChar = char |
|||
} else { |
|||
workingStr += String(char) |
|||
} |
|||
} |
|||
res.append(workingStr) |
|||
return res |
|||
} |
|||
} |
|||
print("gHHH5YY++///\\".splitOnChanges().joined(separator: ", "))</syntaxhighlight> |
|||
{{out}} |
|||
<pre>g, HHH, 5, YY, ++, ///, \</pre> |
|||
=={{header|Tailspin}}== |
|||
<syntaxhighlight lang="tailspin"> |
|||
composer splitEquals |
|||
<reps> <nextReps>* |
|||
rule reps: <'(.)\1*'> |
|||
rule nextReps: <reps> -> \(', ' ! $ ! \) |
|||
end splitEquals |
|||
'gHHH5YY++///\' -> splitEquals -> !OUT::write |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre>g, HHH, 5, YY, ++, ///, \</pre> |
|||
=={{header|tbas}}== |
=={{header|tbas}}== |
||
{{Trans|BBC BASIC}} |
{{Trans|BBC BASIC}} |
||
< |
<syntaxhighlight lang="basic">SUB SPLITUNIQUE$(s$) |
||
DIM c$, d$, split$, i% |
DIM c$, d$, split$, i% |
||
c$ = LEFT$(s$, 1) |
c$ = LEFT$(s$, 1) |
||
Line 1,408: | Line 2,848: | ||
PRINT SPLITUNIQUE$("gHHH5YY++///\") |
PRINT SPLITUNIQUE$("gHHH5YY++///\") |
||
END</ |
END</syntaxhighlight> |
||
=={{header|Tcl}}== |
=={{header|Tcl}}== |
||
{{output?}} |
|||
This is most concise with regular expressions. Note well the two steps: it could be achieved in one very clever regexp, but being that clever is usually a bad idea (for both readability and performance, in this case). |
This is most concise with regular expressions. Note well the two steps: it could be achieved in one very clever regexp, but being that clever is usually a bad idea (for both readability and performance, in this case). |
||
< |
<syntaxhighlight lang="tcl">set string "gHHH5YY++///\\" |
||
regsub -all {(.)\1*} $string {\0, } string |
regsub -all {(.)\1*} $string {\0, } string |
||
regsub {, $} $string {} string |
regsub {, $} $string {} string |
||
puts $string</ |
puts $string</syntaxhighlight> |
||
{{out}} |
|||
<pre>g, HHH, 5, YY, ++, ///, \</pre> |
|||
=={{header|Transd}}== |
|||
The task doesn't state explicitly about the order in which substrings should be |
|||
displayed. So, here are two variants: one is order-preserving, the other is not |
|||
order-preserving. |
|||
<syntaxhighlight lang="Scheme">#lang transd |
|||
MainModule: { |
|||
s: "gHHH5YY++///\\", |
|||
_start: (λ |
|||
(with res "" |
|||
(for c in (split s "") do |
|||
(if (neq Char(c) (back res)) (+= res ", ")) |
|||
(+= res c)) |
|||
(textout res)) |
|||
(lout "Second variant: ") |
|||
(for v in (values (group-by (split s ""))) do |
|||
(textout (if @idx ", ") (join v ""))) |
|||
) |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
g, HHH, 5, YY, ++, ///, \ |
|||
Second variant: |
|||
++, ///, 5, HHH, YY, \, g |
|||
</pre> |
|||
=={{header|Uiua}}== |
|||
{{works with|Uiua|0.11.1}} |
|||
<syntaxhighlight lang="uiua"> |
|||
&p/$"_, _"⊜□+1⊸⊛ "gHHH5YY++///\\" |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
g, HHH, 5, YY, ++, ///, \ |
|||
</pre> |
|||
=={{header|VBA}}== |
=={{header|VBA}}== |
||
<syntaxhighlight lang="vb"> |
|||
<lang vb> |
|||
Option Explicit |
Option Explicit |
||
Line 1,458: | Line 2,937: | ||
Split_Special = R |
Split_Special = R |
||
End Function |
End Function |
||
</syntaxhighlight> |
|||
</lang> |
|||
{{out}} |
{{out}} |
||
<pre>g, HHH, 5, YY, ++, ///, \</pre> |
<pre>g, HHH, 5, YY, ++, ///, \</pre> |
||
=={{header|V (Vlang)}}== |
|||
<syntaxhighlight lang="v (vlang)">fn main() { |
|||
println(splitter('gHHH5YY++///\\')) \\ The "\" character needs to be escaped. |
|||
} |
|||
fn splitter(text string) string { |
|||
mut check := text.substr(0, 1) |
|||
mut new_text, mut temp := '', '' |
|||
for index, _ in text { |
|||
temp = text.substr(index, index + 1) |
|||
if temp != check { |
|||
new_text = new_text + ', ' |
|||
check = temp |
|||
} |
|||
new_text = new_text + temp |
|||
} |
|||
return new_text |
|||
} |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
g, HHH, 5, YY, ++, ///, \ |
|||
</pre> |
|||
=={{header|Wren}}== |
|||
<syntaxhighlight lang="wren">var split = Fn.new { |s| |
|||
if (s.count == 0) return "" |
|||
var res = [] |
|||
var last = s[0] |
|||
var curr = last |
|||
for (c in s.skip(1)) { |
|||
if (c == last) { |
|||
curr = curr + c |
|||
} else { |
|||
res.add(curr) |
|||
curr = c |
|||
} |
|||
last = c |
|||
} |
|||
res.add(curr) |
|||
return res.join(", ") |
|||
} |
|||
var s = "gHHH5YY++///\\" |
|||
System.print(split.call(s))</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
g, HHH, 5, YY, ++, ///, \ |
|||
</pre> |
|||
=={{header|XLISP}}== |
=={{header|XLISP}}== |
||
< |
<syntaxhighlight lang="lisp">(defun delimit (s) |
||
(defun delim (old-list new-list current-char) |
(defun delim (old-list new-list current-char) |
||
(if (null old-list) |
(if (null old-list) |
||
Line 1,474: | Line 3,005: | ||
(list->string (delim (string->list s) '() (car (string->list s)))) ) |
(list->string (delim (string->list s) '() (car (string->list s)))) ) |
||
(display (delimit "gHHH5YY++///\\")) ;; NB. The "\" character needs to be escaped</ |
(display (delimit "gHHH5YY++///\\")) ;; NB. The "\" character needs to be escaped</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre>g, HHH, 5, YY, ++, ///, \</pre> |
<pre>g, HHH, 5, YY, ++, ///, \</pre> |
||
=={{header|XPL0}}== |
|||
<syntaxhighlight lang="xpl0">string 0; \change to zero-terminated convention |
|||
char S; |
|||
[S:= "gHHH5YY++///\"; |
|||
while S(0) do |
|||
[ChOut(0, S(0)); |
|||
if S(1)#S(0) & S(1)#0 then Text(0, ", "); |
|||
S:= S+1; |
|||
]; |
|||
]</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
g, HHH, 5, YY, ++, ///, \ |
|||
</pre> |
|||
=={{header|Yabasic}}== |
|||
<syntaxhighlight lang="freebasic">sub esplit$(instring$) |
|||
if len(instring$) < 2 return instring$ |
|||
ret$ = left$(instring$,1) |
|||
for i = 2 to len(instring$) |
|||
if mid$(instring$,i,1) <> mid$(instring$, i - 1, 1) ret$ = ret$ + ", " |
|||
ret$ = ret$ + mid$(instring$, i, 1) |
|||
next i |
|||
return ret$ |
|||
end sub |
|||
print esplit$("gHHH5YY++///\\")</syntaxhighlight> |
|||
=={{header|Z80 Assembly}}== |
|||
<syntaxhighlight lang="z80">PrintChar equ &BB5A ;Amstrad CPC BIOS call |
|||
Terminator equ 0 ;marks the end of a string |
|||
org &8000 |
|||
LD HL,StringA |
|||
loop: |
|||
ld a,(HL) ;load a char from (HL) |
|||
cp Terminator ;is it the terminator? |
|||
ret z ;if so, exit |
|||
ld e,a ;store this char in E temporarily |
|||
inc hl ;next char |
|||
ld a,(HL) ;get next char |
|||
cp Terminator ;is the next char the terminator? |
|||
jp z,StringDone ;if so, print E and exit. |
|||
;needed to prevent the last char from getting a comma and space. |
|||
dec hl ;go back one so we don't skip any chars |
|||
cp e ;does (HL) == (HL+1)? |
|||
push af |
|||
ld a,e |
|||
call PrintChar ;either way, print E to screen. |
|||
pop af ;retrieve the results of the last compare. |
|||
jr z,SkipComma ;if A=E, no comma or space. Just loop again. |
|||
ld a,',' |
|||
call PrintChar |
|||
ld a,' ' |
|||
call PrintChar |
|||
SkipComma: |
|||
inc hl ;next char |
|||
jp loop ;back to start |
|||
StringDone: |
|||
ld a,e ;last character in string is printed here. |
|||
jp PrintChar |
|||
ReturnToBasic: |
|||
RET |
|||
StringA: |
|||
byte "gHHH5YY++///\",0</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
g, HHH, 5, YY, ++, ///, \ |
|||
</pre> |
|||
=={{header|zkl}}== |
=={{header|zkl}}== |
||
< |
<syntaxhighlight lang="zkl">fcn group(str){ |
||
C,out := str[0],Sink(C); |
C,out := str[0],Sink(C); |
||
foreach c in (str[1,*]){ out.write(if(c==C) c else String(", ",C=c)) } |
foreach c in (str[1,*]){ out.write(if(c==C) c else String(", ",C=c)) } |
||
out.close(); |
out.close(); |
||
} |
} |
||
group("gHHH5YY++///\\").println();</ |
group("gHHH5YY++///\\").println();</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
||
Line 1,491: | Line 3,101: | ||
=={{header|ZX Spectrum Basic}}== |
=={{header|ZX Spectrum Basic}}== |
||
< |
<syntaxhighlight lang="basic"> 10 LET s$="gHHH5YY++///\" |
||
20 LET c$=s$(1) |
20 LET c$=s$(1) |
||
30 LET n$=c$ |
30 LET n$=c$ |
||
Line 1,499: | Line 3,109: | ||
70 LET c$=s$(i) |
70 LET c$=s$(i) |
||
80 NEXT i |
80 NEXT i |
||
90 PRINT n$</ |
90 PRINT n$</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre>g, HHH, 5, YY, ++, ///, \</pre> |
<pre>g, HHH, 5, YY, ++, ///, \</pre> |
Latest revision as of 07:33, 2 July 2024
You are encouraged to solve this task according to the task description, using any language you may know.
- Task
Split a (character) string into comma (plus a blank) delimited strings based on a change of character (left to right).
Show the output here (use the 1st example below).
Blanks should be treated as any other character (except
they are problematic to display clearly). The same applies
to commas.
For instance, the string:
gHHH5YY++///\
should be split and show:
g, HHH, 5, YY, ++, ///, \
- Metrics
- Counting
- Word frequency
- Letter frequency
- Jewels and stones
- I before E except after C
- Bioinformatics/base count
- Count occurrences of a substring
- Count how many vowels and consonants occur in a string
- Remove/replace
- XXXX redacted
- Conjugate a Latin verb
- Remove vowels from a string
- String interpolation (included)
- Strip block comments
- Strip comments from a string
- Strip a set of characters from a string
- Strip whitespace from a string -- top and tail
- Strip control codes and extended characters from a string
- Anagrams/Derangements/shuffling
- Word wheel
- ABC problem
- Sattolo cycle
- Knuth shuffle
- Ordered words
- Superpermutation minimisation
- Textonyms (using a phone text pad)
- Anagrams
- Anagrams/Deranged anagrams
- Permutations/Derangements
- Find/Search/Determine
- ABC words
- Odd words
- Word ladder
- Semordnilap
- Word search
- Wordiff (game)
- String matching
- Tea cup rim text
- Alternade words
- Changeable words
- State name puzzle
- String comparison
- Unique characters
- Unique characters in each string
- Extract file extension
- Levenshtein distance
- Palindrome detection
- Common list elements
- Longest common suffix
- Longest common prefix
- Compare a list of strings
- Longest common substring
- Find common directory path
- Words from neighbour ones
- Change e letters to i in words
- Non-continuous subsequences
- Longest common subsequence
- Longest palindromic substrings
- Longest increasing subsequence
- Words containing "the" substring
- Sum of the digits of n is substring of n
- Determine if a string is numeric
- Determine if a string is collapsible
- Determine if a string is squeezable
- Determine if a string has all unique characters
- Determine if a string has all the same characters
- Longest substrings without repeating characters
- Find words which contains all the vowels
- Find words which contains most consonants
- Find words which contains more than 3 vowels
- Find words which first and last three letters are equals
- Find words which odd letters are consonants and even letters are vowels or vice_versa
- Formatting
- Substring
- Rep-string
- Word wrap
- String case
- Align columns
- Literals/String
- Repeat a string
- Brace expansion
- Brace expansion using ranges
- Reverse a string
- Phrase reversals
- Comma quibbling
- Special characters
- String concatenation
- Substring/Top and tail
- Commatizing numbers
- Reverse words in a string
- Suffixation of decimal numbers
- Long literals, with continuations
- Numerical and alphabetical suffixes
- Abbreviations, easy
- Abbreviations, simple
- Abbreviations, automatic
- Song lyrics/poems/Mad Libs/phrases
- Mad Libs
- Magic 8-ball
- 99 Bottles of Beer
- The Name Game (a song)
- The Old lady swallowed a fly
- The Twelve Days of Christmas
- Tokenize
- Text between
- Tokenize a string
- Word break problem
- Tokenize a string with escaping
- Split a character string based on change of character
- Sequences
11l
F split(input, delim)
V res = ‘’
L(ch) input
I !res.empty & ch != res.last
res ‘’= delim
res ‘’= ch
R res
print(split(‘gHHH5YY++///\’, ‘, ’))
- Output:
g, HHH, 5, YY, ++, ///, \
8080 Assembly
org 100h
jmp demo
;;; Split the string under DE on changing characters,
;;; and store the result at HL.
split: ldax d ; Load character from string
spcopy: mov m,a ; Store in output
cpi '$' ; CP/M string terminator
rz ; Stop when the end is reached
mov b,a ; Store previous character in B
inx d ; Increment input pointer
inx h ; Increment output pointer
ldax d ; Get next character
cmp b ; Same as previous character?
jz spcopy ; Then just copy it
cpi '$' ; Otherwise, if it is the en
jz spcopy ; Then just copy it as well
mvi m,',' ; Otherwise, add a comma and a space
inx h
mvi m,' '
inx h
jmp spcopy
;;; Demo code
demo: lxi d,string
lxi h,out
call split ; Split the string
lxi d,out
mvi c,9 ; And print it using CP/M
jmp 5
string: db 'gHHH5YY++///',5Ch,'$'
out: equ $
- Output:
g, HHH, 5, YY, ++, ///, \
8086 Assembly
cpu 8086
org 100h
section .text
jmp demo
;;; Split the string at DS:SI on changing characters,
;;; and store the result at ES:DI.
split: lodsb ; Load character
.copy: stosb ; Store in output
cmp al,'$' ; Done yet?
je .out ; If so, stop.
mov ah,al ; Store previous character
lodsb ; Get next character
cmp al,ah ; Same character?
je .copy ; Then just copy it
cmp al,'$' ; End of string?
je .copy ; Then just copy it too
mov dl,al
mov ax,', ' ; Otherwise, add a comma and a space
stosw
mov al,dl
jmp .copy
.out: ret
;;; Demo code
demo: mov si,string
mov di,buf
call split ; Split the string
mov dx,buf
mov ah,9
int 21h ; And print the result using DOS
ret
section .data
string: db 'gHHH5YY++///\$'
section .bss
buf: resb 32
- Output:
g, HHH, 5, YY, ++, ///, \
AArch64 Assembly
/* ARM assembly AARCH64 Raspberry PI 3B */
/* program splitcar64.s */
/*******************************************/
/* Constantes file */
/*******************************************/
/* for this file see task include a file in language AArch64 assembly*/
.include "../includeConstantesARM64.inc"
/*********************************/
/* Initialized data */
/*********************************/
.data
szCarriageReturn: .asciz "\n"
szString1: .asciz "gHHH5YY++///\\"
/* IMPORTANT REMARK for compiler as
The way to get special characters into a string is to escape these characters: precede them
with a backslash ‘\’ character. For example ‘\\’ represents one backslash: the first \ is
an escape which tells as to interpret the second character literally as a backslash (which
prevents as from recognizing the second \ as an escape character).
*/
/*********************************/
/* UnInitialized data */
/*********************************/
.bss
sBuffer: .skip 100
/*********************************/
/* code section */
/*********************************/
.text
.global main
main: // entry of program
ldr x0,qAdrszString1 // input string address
ldr x1,qAdrsBuffer // output buffer address
bl split
ldr x0,qAdrsBuffer
bl affichageMess // display message
ldr x0,qAdrszCarriageReturn
bl affichageMess
100: // standard end of the program
mov x0,0 // return code
mov x8,EXIT // request to exit program
svc 0 // perform the system call
qAdrszString1: .quad szString1
qAdrszCarriageReturn: .quad szCarriageReturn
qAdrsBuffer: .quad sBuffer
/******************************************************************/
/* generate value */
/******************************************************************/
/* x0 contains the address of input string */
/* x1 contains the address of output buffer */
split:
stp x1,lr,[sp,-16]! // save registers
mov x4,0 // indice loop input string
mov x5,0 // indice buffer
ldrb w2,[x0,x4] // read first char in reg x2
cbz x2,4f // if null -> end
strb w2,[x1,x5] // store char in buffer
add x5,x5,1 // increment location buffer
1:
ldrb w3,[x0,x4] //read char[x4] in reg x3
cbz x3,4f // if null end
cmp x2,x3 // compare two characters
bne 2f
strb w3,[x1,x5] // = -> store char in buffer
b 3f // loop
2:
mov x2,',' // else store comma in buffer
strb w2,[x1,x5] // store char in buffer
add x5,x5,1
mov x2,' ' // and store space in buffer
strb w2,[x1,x5]
add x5,x5,1
strb w3,[x1,x5] // and store input char in buffer
mov x2,x3 // and maj x2 with new char
3:
add x5,x5,1 // increment indices
add x4,x4,1
b 1b // and loop
4:
strb w3,[x1,x5] // store zero final in buffer
100:
ldp x1,lr,[sp],16 // restaur 2 registers
ret // return to address lr x30
/********************************************************/
/* File Include fonctions */
/********************************************************/
/* for this file see task include a file in language AArch64 assembly */
.include "../includeARM64.inc"
- Output:
gg, HHH, 5, YY, ++, ///, \
Action!
PROC Split(CHAR ARRAY s)
BYTE i
CHAR curr,last
i=1 last=s(1)
Put('")
WHILE i<=s(0)
DO
curr=s(i)
IF curr#last THEN
Print(", ")
FI
Put(curr)
last=curr
i==+1
OD
Put('")
RETURN
PROC Test(CHAR ARRAY s)
PrintF("Input: ""%S""%E",s)
Print("Split: ") Split(s)
PutE() PutE()
RETURN
PROC Main()
Test("gHHH5YY++///\")
Test("gHHH 5++,,,///\")
RETURN
- Output:
Screenshot from Atari 8-bit computer
Input: "gHHH5YY++///\" Split: "g, HHH, 5, YY, ++, ///, \" Input: "gHHH 5++,,,///\" Split: "g, HHH, , 5, ++, ,,,, ///, \"
Ada
with Ada.Text_IO;
procedure Split is
procedure Print_Tokens (s : String) is
i, j : Integer := s'First;
begin
loop
while j<=s'Last and then s(j)=s(i) loop j := j + 1; end loop;
if i/=s'first then Ada.Text_IO.Put (", "); end if;
Ada.Text_IO.Put (s(i..j-1));
i := j;
exit when j>s'last;
end loop;
end Print_Tokens;
begin
Print_Tokens ("gHHH5YY+++");
end split;
ALGOL 68
BEGIN
# returns s with ", " added between each change of character #
PROC split on characters = ( STRING s )STRING:
IF s = "" THEN
# empty string #
""
ELSE
# allow for 3 times as many characters as in the string #
# this would handle a string of unique characters #
[ 3 * ( ( UPB s - LWB s ) + 1 ) ]CHAR result;
INT r pos := LWB result;
CHAR s char := s[ LWB s ];
FOR s pos FROM LWB s TO UPB s DO
IF s char /= s[ s pos ] THEN
# change of character - insert ", " #
result[ r pos ] := ",";
result[ r pos + 1 ] := " ";
r pos +:= 2;
s char := s[ s pos ]
FI;
result[ r pos ] := s[ s pos ];
r pos +:= 1
OD;
# return the used portion of the result #
result[ 1 : r pos - 1 ]
FI ; # split on characters #
print( ( split on characters( "gHHH5YY++///\" ), newline ) )
END
- Output:
g, HHH, 5, YY, ++, ///, \
Amazing Hopper
VERSION 1: string
#include <basico.h>
#define INICIO 1
#define CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\"+-/ \\:,;:_*"
algoritmo
objetivo = "gHHH5YY\"\"++ ///,,,\\", indice=0
largo=0, sublargo=0, v=0
#( largo = len(indice:=(onechar(CHARS,objetivo))) )
t=0, nuevo=""
para cada caracter ( v, indice, largo )
#(t = replicate(v, sublargo := ((poschar(INICIO, v, objetivo) - 1 ) ) ))
#(nuevo = cat( cat(nuevo, t), ", "))
objetivo+=sublargo
siguiente
nuevo -= 2
imprimir( "NEW STRING=\n", nuevo,NL)
terminar
- Output:
$ hopper3 basica/splitrep.bas NEW STRING= g, HHH, 5, YY, "", ++, , ///, ,,,, \
VERSION 2: arrays
#include <basico.h>
#define INICIO 1
#define CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\"+-/ \\:,;:_*"
algoritmo
objetivo = "gHHH5YY\"\"++ ///,,,,\\", indice=0
largo=0, sublargo=0, lista={}, v=0
#( largo = len(indice:=(onechar(CHARS,objetivo))) )
para cada caracter ( v, indice, largo )
#( replicate(v, sublargo := ((poschar(INICIO, v, objetivo) - 1 ))))
meter en( lista )
objetivo+=sublargo
siguiente
imprimir( "LISTA=\n", lista, NL )
terminar
- Output:
$ hopper3 basica/splitrep2.bas LISTA= g,HHH,5,YY,"",++, ,///,,,,,,\
ANSI BASIC
REM >split
DECLARE EXTERNAL FUNCTION FN_split$
PRINT FN_split$( "gHHH5YY++///\" )
END
EXTERNAL FUNCTION FN_split$( s$ )
LET c$ = s$(1:1)
LET split$ = ""
FOR i = 1 TO LEN(s$)
LET d$ = s$(i:i)
IF d$ <> c$ THEN
LET split$ = split$ & ", "
LET c$ = d$
END IF
LET split$ = split$ & d$
NEXT i
LET FN_split$ = split$
END FUNCTION
- Output:
g, HHH, 5, YY, ++, ///, \
APL
split ← 2↓∘∊(⊂', '),¨(⊢≠¯1⌽⊢)⊂⊢
- Output:
split 'gHHH5YY++///\' g, HHH, 5, YY, ++, ///, \
AppleScript
Functional
intercalate(", ", ¬
map(curry(intercalate)'s |λ|(""), ¬
group("gHHH5YY++///\\")))
--> "g, HHH, 5, YY, ++, ///, \\"
-- GENERIC FUNCTIONS ----------------------------------------------------------
-- curry :: (Script|Handler) -> Script
on curry(f)
script
on |λ|(a)
script
on |λ|(b)
|λ|(a, b) of mReturn(f)
end |λ|
end script
end |λ|
end script
end curry
-- foldl :: (a -> b -> a) -> a -> [b] -> a
on foldl(f, startValue, xs)
tell mReturn(f)
set v to startValue
set lng to length of xs
repeat with i from 1 to lng
set v to |λ|(v, item i of xs, i, xs)
end repeat
return v
end tell
end foldl
-- group :: Eq a => [a] -> [[a]]
on group(xs)
script eq
on |λ|(a, b)
a = b
end |λ|
end script
groupBy(eq, xs)
end group
-- groupBy :: (a -> a -> Bool) -> [a] -> [[a]]
on groupBy(f, xs)
set mf to mReturn(f)
script enGroup
on |λ|(a, x)
if length of (active of a) > 0 then
set h to item 1 of active of a
else
set h to missing value
end if
if h is not missing value and mf's |λ|(h, x) then
{active:(active of a) & x, sofar:sofar of a}
else
{active:{x}, sofar:(sofar of a) & {active of a}}
end if
end |λ|
end script
if length of xs > 0 then
tell foldl(enGroup, {active:{item 1 of xs}, sofar:{}}, tail(xs))
if length of (its active) > 0 then
its sofar & its active
else
{}
end if
end tell
else
{}
end if
end groupBy
-- intercalate :: Text -> [Text] -> Text
on intercalate(strText, lstText)
set {dlm, my text item delimiters} to {my text item delimiters, strText}
set strJoined to lstText as text
set my text item delimiters to dlm
return strJoined
end intercalate
-- map :: (a -> b) -> [a] -> [b]
on map(f, xs)
tell mReturn(f)
set lng to length of xs
set lst to {}
repeat with i from 1 to lng
set end of lst to |λ|(item i of xs, i, xs)
end repeat
return lst
end tell
end map
-- Lift 2nd class handler function into 1st class script wrapper
-- mReturn :: Handler -> Script
on mReturn(f)
if class of f is script then
f
else
script
property |λ| : f
end script
end if
end mReturn
-- tail :: [a] -> [a]
on tail(xs)
if length of xs > 1 then
items 2 thru -1 of xs
else
{}
end if
end tail
- Output:
g, HHH, 5, YY, ++, ///, \
Straightforward
(Also case-sensitve.)
on splitAtCharacterChanges(input)
set len to (count input)
if (len < 2) then return input
set chrs to input's characters
set currentChr to beginning of chrs
considering case
repeat with i from 2 to len
set thisChr to item i of chrs
if (thisChr is not currentChr) then
set item i of chrs to ", " & thisChr
set currentChr to thisChr
end if
end repeat
end considering
set astid to AppleScript's text item delimiters
set AppleScript's text item delimiters to ""
set output to chrs as text
set AppleScript's text item delimiters to astid
return output
end splitAtCharacterChanges
-- Test code:
splitAtCharacterChanges("gHHH5YY++///\\")
- Output:
"g, HHH, 5, YY, ++, ///, \\"
ASObjC
use AppleScript version "2.4" -- OS X 10.10 (Yosemite) or later
use framework "Foundation"
on splitAtCharacterChanges(input)
tell (current application's class "NSMutableString"'s stringWithString:(input)) to ¬
return (its stringByReplacingOccurrencesOfString:("(.)\\1*+(?!$)") withString:("$0, ") ¬
options:(current application's NSRegularExpressionSearch) range:({0, its |length|()})) as text
end splitAtCharacterChanges
-- Test code:
splitAtCharacterChanges("gHHH5YY++///\\")
- Output:
"g, HHH, 5, YY, ++, ///, \\"
ARM Assembly
/* ARM assembly Raspberry PI */
/* program splitcar.s */
/************************************/
/* Constantes */
/************************************/
.equ STDOUT, 1 @ Linux output console
.equ EXIT, 1 @ Linux syscall
.equ WRITE, 4 @ Linux syscall
/*********************************/
/* Initialized data */
/*********************************/
.data
szCarriageReturn: .asciz "\n"
szString1: .asciz "gHHH5YY++///\\"
/* IMPORTANT REMARK for compiler as
The way to get special characters into a string is to escape these characters: precede them
with a backslash ‘\’ character. For example ‘\\’ represents one backslash: the first \ is
an escape which tells as to interpret the second character literally as a backslash (which
prevents as from recognizing the second \ as an escape character).
*/
/*********************************/
/* UnInitialized data */
/*********************************/
.bss
sBuffer: .skip 100
/*********************************/
/* code section */
/*********************************/
.text
.global main
main: @ entry of program
ldr r0,iAdrszString1 @ input string address
ldr r1,iAdrsBuffer @ output buffer address
bl split
ldr r0,iAdrsBuffer
bl affichageMess @ display message
ldr r0,iAdrszCarriageReturn
bl affichageMess
100: @ standard end of the program
mov r0, #0 @ return code
mov r7, #EXIT @ request to exit program
svc #0 @ perform the system call
iAdrszString1: .int szString1
iAdrszCarriageReturn: .int szCarriageReturn
iAdrsBuffer: .int sBuffer
/******************************************************************/
/* generate value */
/******************************************************************/
/* r0 contains the address of input string */
/* r1 contains the address of output buffer */
split:
push {r1-r5,lr} @ save registers
mov r4,#0 @ indice loop input string
mov r5,#0 @ indice buffer
ldrb r2,[r0,r4] @ read first char in reg r2
cmp r2,#0 @ if null -> end
beq 3f
strb r2,[r1,r5] @ store char in buffer
add r5,#1 @ increment location buffer
1:
ldrb r3,[r0,r4] @read char[r4] in reg r3
cmp r3,#0 @ if null end
beq 3f
cmp r2,r3 @ compare two characters
streqb r3,[r1,r5] @ = -> store char in buffer
beq 2f @ loop
mov r2,#',' @ else store comma in buffer
strb r2,[r1,r5] @ store char in buffer
add r5,#1
mov r2,#' ' @ and store space in buffer
strb r2,[r1,r5]
add r5,#1
strb r3,[r1,r5] @ and store input char in buffer
mov r2,r3 @ and maj r2 with new char
2:
add r5,#1 @ increment indices
add r4,#1
b 1b @ and loop
3:
strb r3,[r1,r5] @ store zero final in buffer
100:
pop {r1-r5,lr}
bx lr @ return
/******************************************************************/
/* display text with size calculation */
/******************************************************************/
/* r0 contains the address of the message */
affichageMess:
push {r0,r1,r2,r7,lr} @ save registres
mov r2,#0 @ counter length
1: @ loop length calculation
ldrb r1,[r0,r2] @ read octet start position + index
cmp r1,#0 @ if 0 its over
addne r2,r2,#1 @ else add 1 in the length
bne 1b @ and loop
@ so here r2 contains the length of the message
mov r1,r0 @ address message in r1
mov r0,#STDOUT @ code to write to the standard output Linux
mov r7, #WRITE @ code call system "write"
svc #0 @ call systeme
pop {r0,r1,r2,r7,lr} @ restaur des 2 registres */
bx lr @ return
output : gg, HHH, 5, YY, ++, ///, \
Arturo
parts: [] current: ""
loop split {gHHH5YY++///\} 'ch [
if? or? empty? current
contains? current ch -> 'current ++ ch
else [
'parts ++ current
current: new ch
]
]
'parts ++ current
print parts
- Output:
g HHH 5 YY ++ /// \
AutoHotkey
Split_Change(str){
for i, v in StrSplit(str)
res .= (v=prev) ? v : (res?", " :"") v , prev := v
return res
}
Examples:
str := "gHHH5YY++///\"
MsgBox % Split_Change(str)
Outputs:
g, HHH, 5, YY, ++, ///, \
RegEx Version
Split_Change(str){
return RegExReplace(str, "(.)\1*(?!$)", "$0, ")
}
Examples:
str := "gHHH5YY++///\"
MsgBox % Split_Change(str)
Outputs:
g, HHH, 5, YY, ++, ///, \
AWK
# syntax: GAWK -f SPLIT_A_CHARACTER_STRING_BASED_ON_CHANGE_OF_CHARACTER.AWK
BEGIN {
str = "gHHH5YY++///\\"
printf("old: %s\n",str)
printf("new: %s\n",split_on_change(str))
exit(0)
}
function split_on_change(str, c,i,new_str) {
new_str = substr(str,1,1)
for (i=2; i<=length(str); i++) {
c = substr(str,i,1)
if (substr(str,i-1,1) != c) {
new_str = new_str ", "
}
new_str = new_str c
}
return(new_str)
}
- Output:
old: gHHH5YY++///\ new: g, HHH, 5, YY, ++, ///, \
BaCon
Literal strings in BaCon are passed to the C compiler as they are; a backslash therefore needs to be escaped.
txt$ = "gHHH5YY++///\\"
c$ = LEFT$(txt$, 1)
FOR x = 1 TO LEN(txt$)
d$ = MID$(txt$, x, 1)
IF d$ <> c$ THEN
PRINT ", ";
c$ = d$
END IF
PRINT d$;
NEXT
- Output:
g, HHH, 5, YY, ++, ///, \
BASIC256
function split$(instring$)
if length(instring$) < 2 then return instring$
ret$ = left(instring$,1)
for i = 2 to length(instring$)
if mid(instring$,i,1) <> mid(instring$, i-1, 1) then ret$ += ", "
ret$ += mid(instring$, i, 1)
next i
return ret$
end function
print split$("gHHH5YY++///\")
BBC BASIC
REM >split
PRINT FN_split( "gHHH5YY++///\" )
END
DEF FN_split( s$ )
LOCAL c$, split$, d$, i%
c$ = LEFT$( s$, 1 )
split$ = ""
FOR i% = 1 TO LEN s$
LET d$ = MID$( s$, i%, 1 )
IF d$ <> c$ THEN
split$ += ", "
c$ = d$
ENDIF
split$ += d$
NEXT
= split$
- Output:
g, HHH, 5, YY, ++, ///, \
BQN
Split ← (+`⊏⊸»⊸≠)⊸⊔
Join ← {∾⟜𝕨⊸∾´𝕩}
", " Join⟜Split "gHHH5YY++///\"
- Output:
"g, HHH, 5, YY, ++, ///, \"
C
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *split(char *str);
int main(int argc,char **argv)
{
char input[13]="gHHH5YY++///\\";
printf("%s\n",split(input));
}
char *split(char *str)
{
char last=*str,*result=malloc(3*strlen(str)),*counter=result;
for (char *c=str;*c;c++) {
if (*c!=last) {
strcpy(counter,", ");
counter+=2;
last=*c;
}
*counter=*c;
counter++;
}
*(counter--)='\0';
return realloc(result,strlen(result));
}
- Output:
g, HHH, 5, YY, ++, ///, \
C#
using System;
using System.Linq;
using System.Collections.Generic;
public class Program
{
string s = @"gHHH5YY++///\";
Console.WriteLine(s.RunLengthSplit().Delimit(", "));
}
public static class Extensions
{
public static IEnumerable<string> RunLengthSplit(this string source) {
using (var enumerator = source.GetEnumerator()) {
if (!enumerator.MoveNext()) yield break;
char previous = enumerator.Current;
int count = 1;
while (enumerator.MoveNext()) {
if (previous == enumerator.Current) {
count++;
} else {
yield return new string(Enumerable.Repeat(previous, count).ToArray());
previous = enumerator.Current;
count = 1;
}
}
yield return new string(Enumerable.Repeat(previous, count).ToArray());
}
}
public static string Delimit<T>(this IEnumerable<T> source, string separator = "") => string.Join(separator ?? "", source);
}
- Output:
g, HHH, 5, YY, ++, ///, \
C++
// Solution for http://rosettacode.org/wiki/Split_a_character_string_based_on_change_of_character
#include<string>
#include<iostream>
auto split(const std::string& input, const std::string& delim){
std::string res;
for(auto ch : input){
if(!res.empty() && ch != res.back())
res += delim;
res += ch;
}
return res;
}
int main(){
std::cout << split("gHHH5 ))YY++,,,///\\", ", ") << std::endl;
}
- Output:
g, HHH, 5, , )), YY, ++, ,,,, ///, \
Clojure
(defn print-cchanges [s]
(println (clojure.string/join ", " (map first (re-seq #"(.)\1*" s)))))
(print-cchanges "gHHH5YY++///\\")
- Output:
g, HHH, 5, YY, ++, ///, \
CLU
% Split a string based on a change of character
split_on_change = iter (s: string) yields (string)
part: string := ""
for c: char in string$chars(s) do
if ~string$empty(part)
cand part[string$size(part)] ~= c then
yield(part)
part := ""
end
part := part || string$c2s(c)
end
yield(part)
end split_on_change
start_up = proc ()
po: stream := stream$primary_output()
str: string := "gHHH5YYY++///\\" % \\ escapes, as in C
rslt: string := ""
first: bool := true
for part: string in split_on_change(str) do
if first then first := false
else rslt := rslt || ", "
end
rslt := rslt || part
end
stream$putl(po, rslt)
end start_up
- Output:
g, HHH, 5, YYY, ++, ///, \
COBOL
identification division.
program-id. split-ch.
data division.
1 split-str pic x(30) value space.
88 str-1 value "gHHH5YY++///\".
88 str-2 value "gHHH5 ))YY++,,,///\".
1 binary.
2 ptr pic 9(4) value 1.
2 str-start pic 9(4) value 1.
2 delim-len pic 9(4) value 1.
2 split-str-len pic 9(4) value 0.
2 trash-9 pic 9(4) value 0.
1 delim-char pic x value space.
1 delim-str pic x(6) value space.
1 trash-x pic x.
procedure division.
display "Requested string"
set str-1 to true
perform split-init-and-go
display space
display "With spaces and commas"
set str-2 to true
perform split-init-and-go
stop run
.
split-init-and-go.
move 1 to ptr
move 0 to split-str-len
perform split
.
split.
perform get-split-str-len
display split-str (1:split-str-len)
perform until ptr > split-str-len
move ptr to str-start
move split-str (ptr:1) to delim-char
unstring split-str (1:split-str-len)
delimited all delim-char
into trash-x delimiter delim-str
pointer ptr
end-unstring
subtract str-start from ptr giving delim-len
move split-str (str-start:delim-len)
to delim-str (1:delim-len)
display delim-str (1:delim-len) with no advancing
if ptr <= split-str-len
display ", " with no advancing
end-if
end-perform
display space
.
get-split-str-len.
inspect function reverse (split-str) tallying
trash-9 for leading space
split-str-len for characters after space
.
end program split-ch.
- Output:
Requested string gHHH5YY++///\ g, HHH, 5, YY, ++, ///, \ With spaces and commas gHHH5 ))YY++,,,///\ g, HHH, 5, , )), YY, ++, ,,,, ///, \
Common Lisp
(defun split (string)
(loop :for prev := nil :then c
:for c :across string
:do (format t "~:[~;, ~]~c" (and prev (char/= c prev)) c)))
(split "gHHH5YY++///\\")
- Output:
g, HHH, 5, YY, ++, ///, \
Doing more work that what's being ask, the following solution builds a list of strings then output it:
(defun split (string)
(flet ((make-buffer ()
(make-array 0 :element-type 'character :adjustable t :fill-pointer t)))
(loop with buffer = (make-buffer)
with result
for prev = nil then c
for c across string
when (and prev (char/= c prev))
do (push buffer result)
(setf buffer (make-buffer))
do (vector-push-extend c buffer)
finally (push buffer result)
(format t "~{~A~^, ~}"(nreverse result)))))
(split "gHHH5YY++///\\")
- Output:
g, HHH, 5, YY, ++, ///, \
Cowgol
include "cowgol.coh";
sub split(in: [uint8], buf: [uint8]): (out: [uint8]) is
out := buf;
loop
[buf] := [in];
if [in] == 0 then break; end if;
if [in] != [@next in] and [@next in] != 0 then
[buf+1] := ',';
[buf+2] := ' ';
buf := buf+2;
end if;
buf := buf+1;
in := in+1;
end loop;
end sub;
var buf: uint8[32];
print(split("gHHH5YY++//\\", &buf[0]));
print_nl();
- Output:
g, HHH, 5, YY, ++, //, \
D
import std.stdio;
void main() {
auto source = "gHHH5YY++///\\";
char prev = source[0];
foreach(ch; source) {
if (prev != ch) {
prev = ch;
write(", ");
}
write(ch);
}
writeln();
}
- Output:
g, HHH, 5, YY, ++, ///, \
Delphi
function SplitStringCharChange(S: string): string;
{Split string whenever the previous char is different from the current one}
var I: integer;
var C: char;
begin
Result:='';
{Copy string to output}
for I:=1 to Length(S) do
begin
Result:=Result+S[I];
{Appended ", " if the next char is different}
if (I<Length(S)) and (S[I]<>S[I+1]) then Result:=Result+', ';
end;
end;
procedure ShowSplitString(Memo: TMemo);
const S1 = 'gHHH5YY++///\';
var S2: string;
begin
Memo.Lines.Add(S1);
S2:=SplitStringCharChange(S1);
Memo.Lines.Add(S2);
end;
- Output:
gHHH5YY++///\ g, HHH, 5, YY, ++, ///, \ Elapsed Time: 1.767 ms.
Dyalect
func String.SmartSplit() {
var c
var str = ""
var last = this.Length() - 1
for n in 0..last {
if c && this[n] != c {
str += ", "
}
c = this[n]
str += c
}
str
}
print("gHHH5YY++///\\".SmartSplit())
- Output:
g, HHH, 5, YY, ++, ///, \
EasyLang
a$ = "gHHH5YY++///\\"
a$[] = strchars a$
cp$ = a$[1]
for c$ in a$[]
if c$ <> cp$
s$ &= ", "
cp$ = c$
.
s$ &= c$
.
print s$
- Output:
g, HHH, 5, YY, ++, ///, \
ed
# by Artyom Bologov
H
# Repeating two times because matching is greedy
g/.*/s/((.)\2*)((.)\4*)/\1\
\3/g
g/.*/s/((.)\2*)((.)\4*)/\1\
\3/g
,p
Q
- Output:
$ cat split-on-change.ed | ed -lEGs split-on-change.input Newline appended g HHH 5 YY ++ /// \
Elixir
split = fn str ->
IO.puts " input string: #{str}"
String.graphemes(str)
|> Enum.chunk_by(&(&1))
|> Enum.map_join(", ", &Enum.join &1)
|> fn s -> IO.puts "output string: #{s}" end.()
end
split.("gHHH5YY++///\\")
- Output:
input string: gHHH5YY++///\ output string: g, HHH, 5, YY, ++, ///, \
F#
open System.Text.RegularExpressions
let splitRuns s = Regex("""(.)\1*""").Matches(s) |> Seq.cast<Match> |> Seq.map (fun m -> m.Value) |> Seq.toList
printfn "%A" (splitRuns """gHHH5YY++///\""")
- Output:
["g"; "HHH"; "5"; "YY"; "++"; "///"; "\"]
Factor
USE: splitting.monotonic
"gHHH5YY++///\\"
"aaabbccccdeeff" [ [ = ] monotonic-split ", " join print ] bi@
- Output:
g, HHH, 5, YY, ++, ///, \ aaa, bb, cccc, d, ee, ff
Forth
CREATE A 0 ,
: C@A+ A @ C@ [ 1 CHARS ]L A +! ;
: SPLIT. ( c-addr u --) SWAP A ! A @ C@
BEGIN OVER WHILE
C@A+ TUCK <> IF ." , " THEN
DUP EMIT SWAP 1- SWAP
REPEAT DROP ;
: TEST OVER OVER
." input: " TYPE CR
." split: " SPLIT. CR ;
s" gHHH5YY++///\" TEST
s" gHHH5 ))YY++,,,///\" TEST
BYE
- Output:
input: gHHH5YY++///\ split: g, HHH, 5, YY, ++, ///, \ input: gHHH5 ))YY++,,,///\ split: g, HHH, 5, , )), YY, ++, ,,,, ///, \
Fortran
This is F77 style, except for the END SUBROUTINE SPLATTER
which would be just END
, which for F90 is also allowable outside of the MODULE protocol. Linking the start/stop markers by giving the same name is helpful, especially when the compiler checks for this. The $ symbol at the end of a FORMAT code sequence is a common F77 extension, meaning "do not finish the line" so that a later output will follow on. This is acceptable to F90 and is less blather than adding the term ,ADVANCE = "NO"
inside a WRITE statement that would otherwise be required. Output is to I/O unit 6
which is the modern default for "standard output". The format code is A
meaning "any number of characters" rather than A1
for "one character" so as to accommodate not just the single character from TEXT but also the two characters of ", " for the splitter between sequences. Alas, there is no provision to change fount or colour for this, to facilitate the reader's attempts to parse the resulting list especially when the text includes commas or spaces of its own. By contrast, with quoted strings, the standard protocol is to double contained quotes.
An alternative method would be to prepare the entire output in a CHARACTER variable then write that, but this means answering the maddening question "how long is a piece of string?" for that variable, though later Fortran has arrangements whereby a text variable is resized to suit on every assignment, as in TEMP = TEMP // more
- but this means repeatedly copying the text to the new manifestation of the variable. Still another approach would be to prepare an array of fingers to each split point (as in Phrase_reversals#Fortran) so that the final output would be a single WRITE using that array, and again, how big must the array be? At most, as big as the number of characters in TEXT. With F90, subroutines can declare arrays of a size determined on entry, with something like INTEGER A(LEN(TEXT))
If the problem were to be solved by writing a "main line" only, there would have to be a declaration of the text variable there but since a subroutine can receive a CHARACTER variable of any size (the actual size is passed as a secret parameter), this can be dodged.
For this example a DO-loop stepping along the text is convenient, but in a larger context it would probably be most useful to work along the text with fingers L1 and L2 marking the start and finish positions of each sequence.
SUBROUTINE SPLATTER(TEXT) !Print a comma-separated list. Repeated characters constitute one item.
Can't display the inserted commas in a different colour so as not to look like any commas in TEXT.
CHARACTER*(*) TEXT !The text.
INTEGER L !A finger.
CHARACTER*1 C !A state follower.
IF (LEN(TEXT).LE.0) RETURN !Prevent surprises in the following..
C = TEXT(1:1) !Syncopation: what went before.
DO L = 1,LEN(TEXT) !Step through the text.
IF (C.NE.TEXT(L:L)) THEN !A change of character?
C = TEXT(L:L) !Yes. This is the new normal.
WRITE (6,1) ", " !Set off from what went before. This is not from TEXT.
END IF !So much for changes.
WRITE (6,1) C !Roll the current character. (=TEXT(L:L))
1 FORMAT (A,$) !The $ sez: do not end the line.
END DO !On to the next character.
WRITE (6,1) !Thus end the line. No output item means that the $ is not reached, so the line is ended.
END SUBROUTINE SPLATTER !TEXT with spaces, or worse, commas, will produce an odd-looking list.
PROGRAM POKE
CALL SPLATTER("gHHH5YY++///\") !The example given.
END
Unfortunately, the syntax highlighter has failed to notice the terminating quote character, presumably because the preceding backslash might be an "escape sequence" trigger, a facility not used in Fortran text literals except possibly as a later modernist option.
- Output:
g, HHH, 5, YY, ++, ///, \
FreeBASIC
function split( instring as string ) as string
if len(instring) < 2 then return instring
dim as string ret = left(instring,1)
for i as uinteger = 2 to len(instring)
if mid(instring,i,1)<>mid(instring, i - 1, 1) then ret + = ", "
ret += mid(instring, i, 1)
next i
return ret
end function
Frink
s = "gHHH5YY++///\\"
println[join[", ", map[getFunction["first", 1], s =~ %r/((.)\2*)/g]]]
- Output:
g, HHH, 5, YY, ++, ///, \
FutureBasic
FB can process either Pascal strings (slowly being deprecated), or Apple's Core Foundation CFStrings (and Objective-C NSStrings). Here's the old-school Pascal string function:
local fn SplitString( inputStr as Str255 ) as Str255
Str255 resultStr
NSUInteger i
if len$( inputStr ) < 2 then resultStr = inputStr : exit fn
resultStr = left$( inputStr, 1 )
for i = 2 to len$( inputStr )
if mid$( inputStr, i, 1 ) <> mid$( inputStr, i - 1, 1 ) then resultStr = resultStr + ", "
resultStr = resultStr + mid$(inputStr, i, 1)
next
end fn = resultStr
window 1
print fn SplitString( "gHHH5YY++///\" )
HandleEvents
And here's the recommended CFString counterpart:
local fn SplitString( inputStr as CFStringRef ) as CFStringRef
NSUInteger i
unichar chr, lastChr = fn StringCharacterAtIndex( inputStr, 0 )
CFMutableStringRef resultStr = fn MutableStringWithCapacity(0)
for i = 0 to len( inputStr ) - 1
chr = fn StringCharacterAtIndex( inputStr, i )
if ( chr != lastChr ) then MutableStringAppendString( resultStr, @", " )
MutableStringAppendString( resultStr, mid( inputStr, i, 1 ) )
lastChr = chr
next
end fn = resultStr
window 1
print fn SplitString( @"gHHH5YY++///\\" )
HandleEvents
Output for either function:
g, HHH, 5, YY, ++, ///, \
Go
Treating "character" as a byte:
package main
import (
"fmt"
"strings"
)
func main() {
fmt.Println(scc(`gHHH5YY++///\`))
}
func scc(s string) string {
if len(s) < 2 {
return s
}
var b strings.Builder
p := s[0]
b.WriteByte(p)
for _, c := range []byte(s[1:]) {
if c != p {
b.WriteString(", ")
}
b.WriteByte(c)
p = c
}
return b.String()
}
- Output:
g, HHH, 5, YY, ++, ///, \
Haskell
import Data.List (group, intercalate)
main :: IO ()
main = putStrLn $ intercalate ", " (group "gHHH5YY++///\\")
- Output:
g, HHH, 5, YY, ++, ///, \
or as a hand-written fold:
import Data.List (intercalate)
import Data.Bool (bool)
charGroups :: String -> [String]
charGroups =
let go (a, b) (s, groups)
| a == b = (b : s, groups)
| otherwise =
( [a],
bool s [b] (null s) : groups
)
in uncurry (:) . foldr go ([], []) . (zip <*> tail)
main :: IO ()
main =
putStrLn $ intercalate ", " $ charGroups "gHHH5YY++///\\"
g, HHH, 5, YY, ++, ///, \
or in terms of span:
import Data.List (intercalate)
charGroups :: String -> [String]
charGroups [] = []
charGroups (c : cs) =
let (xs, ys) = span (c ==) cs
in (c : xs) : charGroups ys
main :: IO ()
main =
putStrLn $ intercalate ", " $ charGroups "gHHH5YY++///\\"
- Output:
g, HHH, 5, YY, ++, ///, \
IS-BASIC
100 LET S$="gHHH5YY++///\"
110 PRINT S$(1);
120 FOR I=2 TO LEN(S$)
130 IF S$(I)<>S$(I-1) THEN PRINT ", ";
140 PRINT S$(I);
150 NEXT
160 PRINT
J
Solution:
splitChars=: (1 ,~ 2 ~:/\ ]) <;.2 ]
delimitChars=: ', ' joinstring splitChars
Example Usage:
delimitChars 'gHHH5YY++///\'
g, HHH, 5, YY, ++, ///, \
Java
You can use a regular expression to capture every character preceded by 0 or more of itself.
import java.util.regex.Matcher;
import java.util.regex.Pattern;
String split(String string) {
Pattern pattern = Pattern.compile("(.)\\1*");
Matcher matcher = pattern.matcher(string);
StringBuilder strings = new StringBuilder();
int index = 0;
while (matcher.find()) {
if (index++ != 0)
strings.append(", ");
strings.append(matcher.group());
}
return strings.toString();
}
g, HHH, 5, YY, ++, ///, \
An alternate demonstration
package org.rosettacode;
import java.util.ArrayList;
import java.util.List;
/**
* This class provides a main method that will, for each arg provided,
* transform a String into a list of sub-strings, where each contiguous
* series of characters is made into a String, then the next, and so on,
* and then it will output them all separated by a comma and a space.
*/
public class SplitStringByCharacterChange {
public static void main(String... args){
for (String string : args){
List<String> resultStrings = splitStringByCharacter(string);
String output = formatList(resultStrings);
System.out.println(output);
}
}
/**
* @param string String - String to split
* @return List<\String> - substrings of contiguous characters
*/
public static List<String> splitStringByCharacter(String string){
List<String> resultStrings = new ArrayList<>();
StringBuilder currentString = new StringBuilder();
for (int pointer = 0; pointer < string.length(); pointer++){
currentString.append(string.charAt(pointer));
if (pointer == string.length() - 1
|| currentString.charAt(0) != string.charAt(pointer + 1)) {
resultStrings.add(currentString.toString());
currentString = new StringBuilder();
}
}
return resultStrings;
}
/**
* @param list List<\String> - list of strings to format as a comma+space-delimited string
* @return String
*/
public static String formatList(List<String> list){
StringBuilder output = new StringBuilder();
for (int pointer = 0; pointer < list.size(); pointer++){
output.append(list.get(pointer));
if (pointer != list.size() - 1){
output.append(", ");
}
}
return output.toString();
}
}
- Output:
g, HHH, 5, YY, ++, ///, \
JavaScript
ES6
(() => {
"use strict";
// ----------- SPLIT ON CHARACTER CHANGES ------------
const main = () =>
group("gHHH5YY++///\\")
.map(x => x.join(""))
.join(", ");
// --------------------- GENERIC ---------------------
// group :: [a] -> [[a]]
const group = xs =>
// A list of lists, each containing only
// elements equal under (===), such that the
// concatenation of these lists is xs.
groupBy(a => b => a === b)(xs);
// groupBy :: (a -> a -> Bool) [a] -> [[a]]
const groupBy = eqOp =>
// A list of lists, each containing only elements
// equal under the given equality operator,
// such that the concatenation of these lists is xs.
xs => 0 < xs.length ? (() => {
const [h, ...t] = xs;
const [groups, g] = t.reduce(
([gs, a], x) => eqOp(x)(a[0]) ? (
Tuple(gs)([...a, x])
) : Tuple([...gs, a])([x]),
Tuple([])([h])
);
return [...groups, g];
})() : [];
// Tuple (,) :: a -> b -> (a, b)
const Tuple = a =>
b => ({
type: "Tuple",
"0": a,
"1": b,
length: 2,
*[Symbol.iterator]() {
for (const k in this) {
if (!isNaN(k)) {
yield this[k];
}
}
}
});
// MAIN ---
return main();
})();
- Output:
g, HHH, 5, YY, ++, ///, \
Or, in terms of a general `span` function:
(() => {
"use strict";
// -------- STRING SPLIT ON CHARACTER CHANGES --------
// charGroups :: String -> [String]
const charGroups = s =>
// The characters of s split at each point where
// consecutive characters differ.
0 < s.length ? (() => {
const
c = s[0],
[xs, ys] = span(x => c === x)([
...s.slice(1)
]);
return [
[c, ...xs], ...charGroups(ys)
]
.map(zs => [...zs].join(""));
})() : "";
// ---------------------- TEST -----------------------
// main :: IO()
const main = () =>
charGroups("gHHH5YY++///\\")
.join(", ");
// --------------------- GENERIC ---------------------
// span :: (a -> Bool) -> [a] -> ([a], [a])
const span = p =>
// Longest prefix of xs consisting of elements which
// all satisfy p, tupled with the remainder of xs.
xs => {
const i = xs.findIndex(x => !p(x));
return -1 !== i ? [
xs.slice(0, i),
xs.slice(i)
] : [xs, []];
};
// MAIN ---
return main();
})();
- Output:
g, HHH, 5, YY, ++, ///, \
jq
# input: a string
# output: a stream of runs
def runs:
def init:
explode as $s
| $s[0] as $i
| (1 | until( $s[.] != $i; .+1));
if length == 0 then empty
elif length == 1 then .
else init as $n | .[0:$n], (.[$n:] | runs)
end;
"gHHH5YY++///\\" | [runs] | join(", ")
- Output:
Using the -r ("raw output") command-line option of jq:
g, HHH, 5, YY, ++, ///, \
Jsish
Showing off a little unit testing...
Starting with
#!/usr/bin/env jsish
;'Split a string based on change of character, in Jsish';
function splitOnChange(str:string):string {
if (str.length < 2) return str;
var last = str[0];
var result = last;
for (var pos = 1; pos < str.length; pos++) {
result += ((last == str[pos]) ? last : ', ' + str[pos]);
last = str[pos];
}
return result;
}
provide('splitOnChange', 1.0);
/* literal backslash needs escaping during initial processing */
;splitOnChange('gHHH5YY++///\\');
;splitOnChange('a');
;splitOnChange('ab');
;splitOnChange('aaa');
;splitOnChange('aaaba');
;splitOnChange('gH HH5YY++//,/\\');
Then
prompt$ jsish -u -update true splitOnChange.jsi Created splitOnChange.jsi
Giving
#!/usr/bin/env jsish
;'Split a string based on change of character, in Jsish';
function splitOnChange(str:string):string {
if (str.length < 2) return str;
var last = str[0];
var result = last;
for (var pos = 1; pos < str.length; pos++) {
(last == str[pos]) ? result += last : result += ', ' + str[pos];
last = str[pos];
}
return result;
}
provide('splitOnChange', 1.0);
/* literal backslash needs escaping during initial processing */
;splitOnChange('gHHH5YY++///\\');
;splitOnChange('a');
;splitOnChange('ab');
;splitOnChange('aaa');
;splitOnChange('aaaba');
;splitOnChange('gH HH5YY++//,/\\');
/*
=!EXPECTSTART!=
'Split a string based on change of character, in Jsish'
splitOnChange('gHHH5YY++///\') ==> g, HHH, 5, YY, ++, ///, \
splitOnChange('a') ==> a
splitOnChange('ab') ==> a, b
splitOnChange('aaa') ==> aaa
splitOnChange('aaaba') ==> aaa, b, a
splitOnChange('gH HH5YY++//,/\') ==> g, H, , HH, 5, YY, ++, //, ,, /, \
=!EXPECTEND!=
*/
Which tests as:
prompt$ jsish -u splitOnChange.jsi [PASS] splitOnChange.jsi
And then satisfying the task of showing the one result, using the script as a module:
- Output:
prompt$ jsish Jsish interactive: see 'help [cmd]'. \ cancels > input. ctrl-c aborts running script. # require('splitOnChange'); 1 # puts(splitOnChange('gHHH5YY++///\\')); g, HHH, 5, YY, ++, ///, \
Julia
# v0.6
using IterTools
str = "gHHH5YY++///\\"
sep = map(join, groupby(identity, str))
println("string: $str\nseparated: ", join(sep, ", "))
- Output:
string: gHHH5YY++///\ separated: g, HHH, 5, YY, ++, ///, \
K
split: {(&~=':x)_x}
","/ split "gHHH5YY++///\\"
- Output:
"g,HHH,5,YY,++,///,\\"
Kotlin
// version 1.0.6
fun splitOnChange(s: String): String {
if (s.length < 2) return s
var t = s.take(1)
for (i in 1 until s.length)
if (t.last() == s[i]) t += s[i]
else t += ", " + s[i]
return t
}
fun main(args: Array<String>) {
val s = """gHHH5YY++///\"""
println(splitOnChange(s))
}
- Output:
g, HHH, 5, YY, ++, ///, \
Using fold()
fun splitOnChange(src: String): String =
src.fold("") { acc, c ->
if (acc.isEmpty() || acc.last() == c) "$acc$c" else "$acc, $c"
}
fun main() {
splitOnChange("""gHHH5YY++///\""").also { println(it)}
}
- Output:
g, HHH, 5, YY, ++, ///, \
Lambdatalk
{def mysplit
{def mysplit.r
{lambda {:w :i}
{if {> :i {W.length :w}}
then
else {if {not {W.equal? {W.get :i :w} {W.get {+ :i 1} :w}}}
then ____ else} {W.get {+ :i 1} :w}{mysplit.r :w {+ :i 1}}}}}
{lambda {:w}
{S.replace ____ by in {mysplit.r #:w 0}}}}
-> mysplit
{mysplit gHHH5YY++///\}
-> g HHH 5 YY ++ /// \
Lua
Note that the backslash must be quoted as a double backslash as Lua uses C-like escape sequences.
function charSplit (inStr)
local outStr, nextChar = inStr:sub(1, 1)
for pos = 2, #inStr do
nextChar = inStr:sub(pos, pos)
if nextChar ~= outStr:sub(#outStr, #outStr) then
outStr = outStr .. ", "
end
outStr = outStr .. nextChar
end
return outStr
end
print(charSplit("gHHH5YY++///\\"))
- Output:
g, HHH, 5, YY, ++, ///, \
Alternative: Simply scan difference in reverse order and insert delimiter in place, the loop counter i will not update with length of s.
function splitdiff(s)
for i=#s,2,-1 do
if s:sub(i,i)~=s:sub(i-1,i-1) then
s = s:sub(1,i-1)..', '.. s:sub(i,-1)
end
end
return s
end
Ksh
#!/bin/ksh
# Split a character string based on change of character
# # Variables:
#
str='gHHH5YY++///\'
delim=', '
# # Functions:
#
# # Function _splitonchg(str, delim) - return str split by delim at char change
#
function _splitonchg {
typeset _str ; _str="$1"
typeset _delim ; _delim="$2"
typeset _i _splitstr ; integer _i
for ((_i=1; _i<${#_str}+1; _i++)); do
if [[ "${_str:$((_i-1)):1}" != "${_str:${_i}:1}" ]]; then
_splitstr+="${_str:$((_i-1)):1}${_delim}"
else
_splitstr+="${_str:$((_i-1)):1}"
fi
done
echo "${_splitstr%"${_delim}"*}"
}
######
# main #
######
print "Original: ${str}"
print " Split: $(_splitonchg "${str}" "${delim}")"
- Output:
Original: gHHH5YY++///\
Split: g, HHH, 5, YY, ++, ///, \
M2000 Interpreter
Stack New open a new stack object as current stack, and keep the old one. After the end of block execution old stack get back as current stack. Data statement push to bottom (we read from top, so using data we get a FIFO type). Letter$ pops a string or raise an error if no string found at the top of stack.
Module PrintParts(splitthis$) {
Def string m$, p$
Def long c
Stack New {
if len(splitthis$)=0 then exit
For i=1 to len(splitthis$)
p$=mid$(splitthis$,i,1)
if m$<>p$ then {
if c>0 then data string$(m$, c)
m$=p$
c=1
} else c++
Next i
if c>0 then data string$(m$, c)
While stack.size>1 {
Print letter$+", ";
}
If not empty then Print letter$
}
}
PrintParts "gHHH5YY++///\"
Maple
Added an additional backlash to escape the \ character at the end.
splitChange := proc(str::string)
local start,i,len;
start := 1;
len := StringTools:-Length(str);
for i from 2 to len do
if str[i] <> str[start] then
printf("%s, ", str[start..i-1]);
start := i:
end if;
end do;
printf("%s", str[start..len]);
end proc;
splitChange("gHHH5YY++///\\");
- Output:
g, HHH, 5, YY, ++, ///, \
Mathematica/Wolfram Language
The backslash (\) must be escaped with another backslash when defining the string.
StringJoin@@Riffle[StringCases["gHHH5YY++///\\", p : (x_) .. -> p], ", "]
- Output:
g, HHH, 5, YY, ++, ///, \
MiniScript
s = "gHHH5YY++///\"
output = []
lastLetter = s[0]
for letter in s
if letter != lastLetter then output.push ", "
output.push letter
lastLetter = letter
end for
print output.join("")
- Output:
g, HHH, 5, YY, ++, ///, \
Modula-2
MODULE CharacterChange;
FROM Terminal IMPORT Write,WriteString,WriteLn,ReadChar;
PROCEDURE Split(str : ARRAY OF CHAR);
VAR
i : CARDINAL;
c : CHAR;
BEGIN
FOR i:=0 TO HIGH(str) DO
IF i=0 THEN
c := str[i]
ELSIF str[i]#c THEN
c := str[i];
WriteLn;
END;
Write(c)
END
END Split;
CONST EX = "gHHH5YY++///\";
BEGIN
Split(EX);
ReadChar
END CharacterChange.
- Output:
g HHH 5 YY ++ /// \
Nim
proc splitOnDiff(str: string): string =
result = ""
if str.len < 1: return result
var prevChar: char = str[0]
for idx in 0 ..< str.len:
if str[idx] != prevChar:
result &= ", "
prevChar = str[idx]
result &= str[idx]
assert splitOnDiff("""X""") == """X"""
assert splitOnDiff("""XX""") == """XX"""
assert splitOnDiff("""XY""") == """X, Y"""
assert splitOnDiff("""gHHH5YY++///\""") == """g, HHH, 5, YY, ++, ///, \"""
echo splitOnDiff("""gHHH5YY++///\""")
- Output:
g, HHH, 5, YY, ++, ///, \
ooRexx
Parse Arg str . /*obtain optional arguments from the CL*/
If str=='' Then str= 'gHHH5YY++///\' /*Not specified? Then use the default.*/
i=1
ol=''
Do Forever
j=verify(str,substr(str,i,1),'N',i,99) /* find first character that's different */
If j=0 Then Do /* End of strin reached */
ol=ol||substr(str,i) /* the final substring */
Leave
End
ol=ol||substr(str,i,j-i)', ' /* add substring and delimiter */
i=j
End
Say ol
- Output:
g, HHH, 5, YY, ++, ///, \
Pascal
program SplitChars;
{$IFDEF FPC}
{$MODE DELPHI}{$COPERATORS ON}
{$ENDIF}
const
TestString = 'gHHH5YY++///\';
function SplitAtChars(const S: String):String;
var
i : integer;
lastChar:Char;
begin
result := '';
IF length(s) > 0 then
begin
LastChar := s[1];
result := LastChar;
For i := 2 to length(s) do
begin
if s[i] <> lastChar then
begin
lastChar := s[i];
result += ', ';
end;
result += LastChar;
end;
end;
end;
BEGIN
writeln(SplitAtChars(TestString));
end.
- Output:
g, HHH, 5, YY, ++, ///, \
Perl
use strict;
use warnings;
use feature 'say';
use utf8;
binmode(STDOUT, ':utf8');
for my $string (q[gHHH5YY++///\\], q[fffn⃗n⃗n⃗»»» ℵℵ☄☄☃☃̂☃🤔🇺🇸🤦♂️👨👩👧👦]) {
my @S;
my $last = '';
while ($string =~ /(\X)/g) {
if ($last eq $1) { $S[-1] .= $1 } else { push @S, $1 }
$last = $1;
}
say "Orginal: $string\n Split: 「" . join('」, 「', @S) . "」\n";
}
- Output:
Orginal: gHHH5YY++///\ Split: 「g」, 「HHH」, 「5」, 「YY」, 「++」, 「///」, 「\」 Orginal: fffn⃗n⃗n⃗»»» ℵℵ☄☄☃☃̂☃🤔🇺🇸🤦♂️👨👩👧👦 Split: 「fff」, 「」, 「n⃗n⃗n⃗」, 「»»»」, 「 」, 「ℵℵ」, 「☄☄」, 「☃」, 「☃̂」, 「☃」, 「🤔」, 「🇺🇸」, 「🤦♂️」, 「👨👩👧👦」
Phix
function split_on_change(string s) string res = "" if length(s) then integer prev = s[1] for i=1 to length(s) do integer ch = s[i] if ch!=prev then res &= ", " prev = ch end if res &= ch end for end if return res end function puts(1,split_on_change(`gHHH5YY++///\`))
- Output:
g, HHH, 5, YY, ++, ///, \
Phixmonti
/# Rosetta Code problem: https://rosettacode.org/wiki/Split_a_character_string_based_on_change_of_character
by Galileo, 11/2022 #/
include ..\Utilitys.pmt
""
"gHHH5YY++///\" 1 get >ps
len for get
dup tps == if
rot swap chain swap
else
ps> drop >ps
swap ", " tps chain chain swap
endif
endfor
pstack
- Output:
["g, HHH, 5, YY, ++, ///, \", "gHHH5YY++///\"] === Press any key to exit ===
PicoLisp
(de splitme (Str)
(let (Str (chop Str) Fin)
(glue
", "
(make
(for X Str
(if (= X (car Fin))
(conc Fin (cons X))
(link (setq Fin (cons X))) ) ) ) ) ) )
(prinl (splitme "gHHH5YY++///\\"))
- Output:
g, HHH, 5, YY, ++, ///, \
Pike
string input = "gHHH5YY++///\\"; // \ needs escaping
string last_char;
foreach(input/1, string char) {
if(last_char && char != last_char)
write(", ");
write(char);
last_char = char;
}
- Output:
g, HHH, 5, YY, ++, ///, \
Plain English
To make sense of this example, you must understand riders. A rider is a simple abstraction for efficiently parsing strings. A rider is a record with an original substring, a source substring, and a token substring.
After executing the following code, for example:
Put "abcdef" into a string.
Slap a rider on the string.
The rider looks like this:
Original: "abcdef"
Source: "abcdef"
Token: ""
Now when we Bump the rider.
, it looks like this:
Original: "abcdef"
Source: "bcdef"
Token: "a"
Another bump, and:
Original: "abcdef"
Source: "cdef"
Token: "ab"
Now let's say we have a complete token and want to start a new one. We can
Position the rider's token on the rider's source.
and now the rider looks like this:
Original: "abcdef"
Source: "cdef"
Token: ""
And that's all there is to it.
To run:
Start up.
Split "gHHH5YY++///\" into some string things by change of character.
Write the string things on the console.
Destroy the string things.
Wait for the escape key.
Shut down.
To split a string into some string things by change of character:
If the string's length is less than 2, add the string to the string things; exit.
Slap a rider on the string.
Loop.
Move the rider (change of character rules).
Add the rider's token to the string things.
If the rider's source is blank, exit.
Repeat.
To move a rider (change of character rules):
Position the rider's token on the rider's source.
Loop.
If the rider's source is blank, exit.
If the rider's token is blank, bump the rider; repeat.
Put the rider's token's last plus 1 into a byte pointer.
If the rider's token's last's target is not the byte pointer's target, exit.
Bump the rider.
Repeat.
To write some string things to a console;
To write some string things on a console:
Get a string thing from the string things.
Loop.
If the string thing is nil, write "" on the console; exit.
Write the string thing's string on the console without advancing.
If the string thing's next is not nil, write ", " on the console without advancing.
Put the string thing's next into the string thing.
Repeat.
- Output:
g, HHH, 5, YY, ++, ///, \
PowerShell
function Split-String ([string]$String)
{
[string]$c = $String.Substring(0,1)
[string]$splitString = $c
for ($i = 1; $i -lt $String.Length; $i++)
{
[string]$d = $String.Substring($i,1)
if ($d -ne $c)
{
$splitString += ", "
$c = $d
}
$splitString += $d
}
$splitString
}
Split-String "gHHH5YY++///\"
- Output:
g, HHH, 5, YY, ++, ///, \
PureBasic
Procedure splitstring(s$)
Define *p.Character = @s$,
c_buf.c = *p\c
While *p\c
If *p\c = c_buf
Print(Chr(c_buf))
Else
Print(", ")
c_buf = *p\c
Continue
EndIf
*p + SizeOf(Character)
Wend
EndProcedure
If OpenConsole()
splitstring("gHHH5YY++///\")
Input()
EndIf
- Output:
g, HHH, 5, YY, ++, ///, \
Python
Python3.6+
Using [itertools.groupby].
from itertools import groupby
def splitter(text):
return ', '.join(''.join(group) for key, group in groupby(text))
if __name__ == '__main__':
txt = 'gHHH5YY++///\\' # Note backslash is the Python escape char.
print(f'Input: {txt}\nSplit: {splitter(txt)}')
- Output:
Input: gHHH5YY++///\ Split: g, HHH, 5, YY, ++, ///, \
Python: Using zip
def splitterz(text):
return (''.join(x + ('' if x == nxt else ', ')
for x, nxt in zip(txt, txt[1:] + txt[-1])))
if __name__ == '__main__':
txt = 'gHHH5YY++///\\'
print(splitterz(txt))
- Output:
g, HHH, 5, YY, ++, ///, \
Python2
import itertools
try: input = raw_input
except: pass
s = input()
groups = []
for _, g in itertools.groupby(s):
groups.append(''.join(g))
print(' input string: %s' % s)
print(' output string: %s' % ', '.join(groups))
- Output:
when using the default input
input string: gHHH5YY++///\ output string: g, HHH, 5, YY, ++, ///, \
Quackery
[ dup size 2 <
iff size done
behead swap
[] nested join
witheach
[ over != if
[ drop i^ 1+
conclude ] ] ] is $run ( $ --> n )
[ dup size 2 < if done
dup $run split
dup [] =
iff drop done
dip [ $ ", " join ]
recurse join ] is runs$ ( $ --> $ )
Testing in Quackery shell.
/O> $ "gHHH5YY++///\" runs$ echo$ ... g, HHH, 5, YY, ++, ///, \ Stack empty.
Racket
#lang racket
(define (split-strings-on-change s)
(map list->string (group-by values (string->list s) char=?)))
(displayln (string-join (split-strings-on-change #<<<
gHHH5YY++///\
<
)
", "))
- Output:
g, HHH, 5, YY, ++, ///, \
Raku
(formerly Perl 6)
sub group-chars ($str) { $str.comb: / (.) $0* / }
# Testing:
for Q[gHHH5YY++///\], Q[fffn⃗n⃗n⃗»»» ℵℵ☄☄☃☃̂☃🤔🇺🇸🤦♂️👨👩👧👦] -> $string {
put 'Original: ', $string;
put ' Split: ', group-chars($string).join(', ');
}
- Output:
Original: gHHH5YY++///\ Split: g, HHH, 5, YY, ++, ///, \ Original: fffn⃗n⃗n⃗»»» ℵℵ☄☄☃☃̂☃🤔🇺🇸🤦♂️👨👩👧👦 Split: fff, , n⃗n⃗n⃗, »»», , ℵℵ, ☄☄, ☃, ☃̂, ☃, 🤔, 🇺🇸, 🤦♂️, 👨👩👧👦
The second test-case is to show that Raku works with strings on the Unicode grapheme level, handles whitespace, combiners, and zero width characters up to Unicode Version 13.0 correctly. (Raku generally tracks updates to the Unicode spec and typically lags no more than a month behind.) For those of you with browsers unable to display the second string, it consists of:
- {LATIN SMALL LETTER F} x 3
- {ZERO WIDTH NO-BREAK SPACE} x 3
- {LATIN SMALL LETTER N, COMBINING RIGHT ARROW ABOVE} x 3
- {RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK} x 3
- {SPACE} x 2,
- {ALEF SYMBOL} x 2,
- {COMET} x 2,
- {SNOWMAN} x 1,
- {SNOWMAN, COMBINING CIRCUMFLEX ACCENT} x 1
- {SNOWMAN} x 1,
- {THINKING FACE} x 1
- {REGIONAL INDICATOR SYMBOL LETTER U, REGIONAL INDICATOR SYMBOL LETTER S} x 1
- {FACE PALM, ZERO WIDTH JOINER, MALE SIGN, VARIATION SELECTOR-16} x 1
- {MAN, ZERO WIDTH JOINER, WOMAN, ZERO WIDTH JOINER, GIRL, ZERO WIDTH JOINER, BOY} x 1
Refal
$ENTRY Go {
= <Prout <Join (', ') <Split 'gHHH5YY++///\\'>>>;
};
Split {
(e.Cur) = (e.Cur);
(e.Cur s.1) s.1 e.X = <Split (e.Cur s.1 s.1) e.X>;
(e.Cur) s.1 e.X = (e.Cur) <Split (s.1) e.X>;
s.1 e.X = <Split (s.1) e.X>;
};
Join {
(e.Joiner) = ;
(e.Joiner) (e.Str) = e.Str;
(e.Joiner) (e.Str) e.Strs = e.Str e.Joiner <Join (e.Joiner) e.Strs>;
};
- Output:
g, HHH, 5, YY, ++, ///, \
REXX
version 1
/*REXX program splits a string based on change of character ───► a comma delimited list.*/
parse arg str /*obtain optional arguments from the CL*/
if str=='' then str= 'gHHH5YY++///\' /*Not specified? Then use the default.*/
p=left(str, 1) /*placeholder for the "previous" string*/
$= /* " " " output " */
do j=1 for length(str); @=substr(str,j,1) /*obtain a character from the string. */
if @\==p then $=$', ' /*Not replicated char? Append delimiter*/
p=@; $=$ || @ /*append a character to the $ string.*/
end /*j*/ /* [↓] keep peeling chars until done. */
say ' input string: ' str /*display the original string & output.*/
say ' output string: ' $ /*stick a fork in it, we're all done. */
- output when using the default input:
input string: gHHH5YY++///\ output string: g, HHH, 5, YY, ++, ///, \
version 2
/* REXX */
Parse arg str /*obtain optional arguments from the CL*/
if str=='' then str= 'gHHH5YY++///\' /*Not specified? Then use the default.*/
input=str
x=''
cp=''
result=''
Do While str<>''
Parse Var str c +1 str
If c==cp Then x=x||c
Else Do
If x>>'' Then
result=result||x', '
x=c
End
cp=c
End
result=result||x
say ' input string: ' input
say ' output string: ' result
{{out]]
input string: gHHH5YY++///\ output string: g, HHH, 5, YY, ++, ///, \
Ring
see split("gHHH5YY++///\")
func split(s )
c =left (s, 1)
split = ""
for i = 1 to len(s)
d = substr(s, i, 1)
if d != c
split = split + ", "
c = d
ok
split = split + d
next
return split
Output:
g, HHH, 5, YY, ++, ///, \
RPL
≪ → text
≪ "" text 1 1 SUB
1 text SIZE FOR j
text j DUP SUB
IF DUP2 ≠ THEN SWAP DROP ", " OVER + END
ROT SWAP + SWAP
NEXT DROP
≫ ≫ ‘COMASPLT’ STO
Ruby
def split(str)
puts " input string: #{str}"
s = str.chars.chunk(&:itself).map{|_,a| a.join}.join(", ")
puts "output string: #{s}"
s
end
split("gHHH5YY++///\\")
- Output:
input string: gHHH5YY++///\ output string: g, HHH, 5, YY, ++, ///, \
Rust
fn splitter(string: &str) -> String {
let chars: Vec<_> = string.chars().collect();
let mut result = Vec::new();
let mut last_mismatch = 0;
for i in 0..chars.len() {
if chars.len() == 1 {
return chars[0..1].iter().collect();
}
if i > 0 && chars[i-1] != chars[i] {
let temp_result: String = chars[last_mismatch..i].iter().collect();
result.push(temp_result);
last_mismatch = i;
}
if i == chars.len() - 1 {
let temp_result: String = chars[last_mismatch..chars.len()].iter().collect();
result.push(temp_result);
}
}
result.join(", ")
}
fn main() {
let test_string = "g";
println!("input string: {}", test_string);
println!("output string: {}", splitter(test_string));
let test_string = "";
println!("input string: {}", test_string);
println!("output string: {}", splitter(test_string));
let test_string = "gHHH5YY++///\\";
println!("input string: {}", test_string);
println!("output string: {}", splitter(test_string));
}
- Output:
input string: g output string: g input string: output string: input string: gHHH5YY++///\ output string: g, HHH, 5, YY, ++, ///, \
Alternate using IterTools
use itertools::Itertools;
pub fn split_text(s: &str) -> Vec<String> {
let mut r = Vec::new();
for (_, group) in &s.chars().into_iter().group_by(|e| *e) {
r.push(group.map(|e| e.to_string()).join(""));
}
r
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_splitting_text() {
assert_eq!(split_text("gHHH5YY++///\\"), vec!["g", "HHH", "5", "YY", "++", "///", "\\"]);
assert!(split_text("").is_empty());
}
}
Scala
// Split a (character) string into comma (plus a blank) delimited strings
// based on a change of character (left to right).
// See https://rosettacode.org/wiki/Split_a_character_string_based_on_change_of_character#Scala
def runLengthSplit(s: String): String = /// Add a guard letter
(s + 'X').sliding(2).map(pair => pair.head + (if (pair.head != pair.last) ", " else "")).mkString("")
println(runLengthSplit("""gHHH5YY++///\"""))
- Output:
See it in running in your browser by ScalaFiddle (JavaScript)
or by Scastie (JVM).
def runLengthSplit(s:String):List[String] = {
def recursiveSplit(acc:List[String], rest:String): List[String] = rest match {
case "" => acc
case _ => {
val (h, t) = rest.span(_ == rest.head)
recursiveSplit(acc :+ h, t)
}
}
recursiveSplit(Nil, s)
}
val result = runLengthSplit("""gHHH5YY++///\""")
println(result.mkString(","))
- Output:
g,HHH,5,YY,++,///,\
Sed
echo 'gHHH5YY++///\' | sed 's/\(.\)\1*/&, /g;s/, $//'
Output:
g, HHH, 5, YY, ++, ///, \
SETL
program split_a_character_string_based_on_change_of_character;
s := "gHHH5YY++///\\";
print(join_strings(", ", split_on_change(s)));
proc split_on_change(s);
parts := [];
loop while s /= "" do
parts with:= span(s, s(1));
end loop;
return parts;
end proc;
proc join_strings(s, parts);
if parts=[] then return ""; end if;
return parts(1) +/ [s + part : part in parts(2..)];
end proc;
end program;
- Output:
g, HHH, 5, YY, ++, ///, \
Sidef
func group(str) {
gather {
while (var match = (str =~ /((.)\g{-1}*)/g)) {
take(match[0])
}
}
}
say group(ARGV[0] \\ 'gHHH5YY++///\\').join(', ')
- Output:
g, HHH, 5, YY, ++, ///, \
SNOBOL4
* Program: split_on_change_of_character.sbl
* To run: sbl split_on_change_of_character.sbl
* Description: Split a (character) string into comma (plus a blank)
* delimited strings based on a change of character (left to right).
*
* Blanks should be treated as any other character
* (except they are problematic to display clearly).
* The same applies to commas.
*
* For instance, the string:
*
* gHHH5YY++///\
* should be split and show:
*
* g, HHH, 5, YY, ++, ///, \
* Comment: Tested using the Spitbol for Linux version of SNOBOL4
lf = substr(&alphabet,11,1) ;* New line or line feed
* Function split_cc will split a string on a change of character.
define('split_cc(s)tchar,target,post')
:(split_cc_end)
split_cc
tchar = substr(s,1,1) :f(freturn)
split_cc_pat = span(*tchar) . target (rpos(0) | len(1) . tchar rem) . post
split_cc2
s ? split_cc_pat = post :f(split_cc3)
split_cc = (ident(split_cc) target, split_cc ', ' target) :s(split_cc2)
split_cc3
:(return)
split_cc_end
test_string = "gHHH5YY++///\"
output = test_string lf
split_string = split_cc(test_string)
output = split_string
END
- Output:
gHHH5YY++///\ g, HHH, 5, YY, ++, ///, \
Standard ML
(*
* Head-Tail implementation of grouping
*)
fun group' ac nil = [ac]
| group' nil (y::ys) = group' [y] ys
| group' (x::ac) (y::ys) = if x=y then group' (y::x::ac) ys else (x::ac) :: group' [y] ys
fun group xs = group' nil xs
fun groupString str = String.concatWith ", " (map implode (group (explode str)))
- Output:
- groupString "gHHH5YY++///\\"; val it = "g, HHH, 5, YY, ++, ///, \\" : string
Swift
public extension String {
func splitOnChanges() -> [String] {
guard !isEmpty else {
return []
}
var res = [String]()
var workingChar = first!
var workingStr = "\(workingChar)"
for char in dropFirst() {
if char != workingChar {
res.append(workingStr)
workingStr = "\(char)"
workingChar = char
} else {
workingStr += String(char)
}
}
res.append(workingStr)
return res
}
}
print("gHHH5YY++///\\".splitOnChanges().joined(separator: ", "))
- Output:
g, HHH, 5, YY, ++, ///, \
Tailspin
composer splitEquals
<reps> <nextReps>*
rule reps: <'(.)\1*'>
rule nextReps: <reps> -> \(', ' ! $ ! \)
end splitEquals
'gHHH5YY++///\' -> splitEquals -> !OUT::write
- Output:
g, HHH, 5, YY, ++, ///, \
tbas
SUB SPLITUNIQUE$(s$)
DIM c$, d$, split$, i%
c$ = LEFT$(s$, 1)
split$ = ""
FOR i% = 1 TO LEN(s$)
d$ = MID$(s$, i%, 1)
IF d$ <> c$ THEN
split$ = split$ + ", "
c$ = d$
END IF
split$ = split$ + d$
NEXT
RETURN split$
END SUB
PRINT SPLITUNIQUE$("gHHH5YY++///\")
END
Tcl
This is most concise with regular expressions. Note well the two steps: it could be achieved in one very clever regexp, but being that clever is usually a bad idea (for both readability and performance, in this case).
set string "gHHH5YY++///\\"
regsub -all {(.)\1*} $string {\0, } string
regsub {, $} $string {} string
puts $string
- Output:
g, HHH, 5, YY, ++, ///, \
Transd
The task doesn't state explicitly about the order in which substrings should be displayed. So, here are two variants: one is order-preserving, the other is not order-preserving.
#lang transd
MainModule: {
s: "gHHH5YY++///\\",
_start: (λ
(with res ""
(for c in (split s "") do
(if (neq Char(c) (back res)) (+= res ", "))
(+= res c))
(textout res))
(lout "Second variant: ")
(for v in (values (group-by (split s ""))) do
(textout (if @idx ", ") (join v "")))
)
}
- Output:
g, HHH, 5, YY, ++, ///, \ Second variant: ++, ///, 5, HHH, YY, \, g
Uiua
&p/$"_, _"⊜□+1⊸⊛ "gHHH5YY++///\\"
- Output:
g, HHH, 5, YY, ++, ///, \
VBA
Option Explicit
Sub Split_string_based_on_change_character()
Dim myArr() As String, T As String
Const STRINPUT As String = "gHHH5YY++///\"
Const SEP As String = ", "
myArr = Split_Special(STRINPUT)
T = Join(myArr, SEP)
Debug.Print Left(T, Len(T) - Len(SEP))
End Sub
Function Split_Special(Ch As String) As String()
'return an array of Strings
Dim tb, i&, st As String, cpt As Long, R() As String
tb = Split(StrConv(Ch, vbUnicode), Chr(0))
st = tb(LBound(tb))
ReDim R(cpt)
R(cpt) = st
For i = 1 To UBound(tb)
If tb(i) = st Then
R(cpt) = R(cpt) & st
Else
st = tb(i)
cpt = cpt + 1
ReDim Preserve R(cpt)
R(cpt) = st
End If
Next
Split_Special = R
End Function
- Output:
g, HHH, 5, YY, ++, ///, \
V (Vlang)
fn main() {
println(splitter('gHHH5YY++///\\')) \\ The "\" character needs to be escaped.
}
fn splitter(text string) string {
mut check := text.substr(0, 1)
mut new_text, mut temp := '', ''
for index, _ in text {
temp = text.substr(index, index + 1)
if temp != check {
new_text = new_text + ', '
check = temp
}
new_text = new_text + temp
}
return new_text
}
- Output:
g, HHH, 5, YY, ++, ///, \
Wren
var split = Fn.new { |s|
if (s.count == 0) return ""
var res = []
var last = s[0]
var curr = last
for (c in s.skip(1)) {
if (c == last) {
curr = curr + c
} else {
res.add(curr)
curr = c
}
last = c
}
res.add(curr)
return res.join(", ")
}
var s = "gHHH5YY++///\\"
System.print(split.call(s))
- Output:
g, HHH, 5, YY, ++, ///, \
XLISP
(defun delimit (s)
(defun delim (old-list new-list current-char)
(if (null old-list)
new-list
(delim (cdr old-list) (append new-list
(if (not (equal (car old-list) current-char))
`(#\, #\Space ,(car old-list))
(cons (car old-list) nil) ) )
(car old-list) ) ) )
(list->string (delim (string->list s) '() (car (string->list s)))) )
(display (delimit "gHHH5YY++///\\")) ;; NB. The "\" character needs to be escaped
- Output:
g, HHH, 5, YY, ++, ///, \
XPL0
string 0; \change to zero-terminated convention
char S;
[S:= "gHHH5YY++///\";
while S(0) do
[ChOut(0, S(0));
if S(1)#S(0) & S(1)#0 then Text(0, ", ");
S:= S+1;
];
]
- Output:
g, HHH, 5, YY, ++, ///, \
Yabasic
sub esplit$(instring$)
if len(instring$) < 2 return instring$
ret$ = left$(instring$,1)
for i = 2 to len(instring$)
if mid$(instring$,i,1) <> mid$(instring$, i - 1, 1) ret$ = ret$ + ", "
ret$ = ret$ + mid$(instring$, i, 1)
next i
return ret$
end sub
print esplit$("gHHH5YY++///\\")
Z80 Assembly
PrintChar equ &BB5A ;Amstrad CPC BIOS call
Terminator equ 0 ;marks the end of a string
org &8000
LD HL,StringA
loop:
ld a,(HL) ;load a char from (HL)
cp Terminator ;is it the terminator?
ret z ;if so, exit
ld e,a ;store this char in E temporarily
inc hl ;next char
ld a,(HL) ;get next char
cp Terminator ;is the next char the terminator?
jp z,StringDone ;if so, print E and exit.
;needed to prevent the last char from getting a comma and space.
dec hl ;go back one so we don't skip any chars
cp e ;does (HL) == (HL+1)?
push af
ld a,e
call PrintChar ;either way, print E to screen.
pop af ;retrieve the results of the last compare.
jr z,SkipComma ;if A=E, no comma or space. Just loop again.
ld a,','
call PrintChar
ld a,' '
call PrintChar
SkipComma:
inc hl ;next char
jp loop ;back to start
StringDone:
ld a,e ;last character in string is printed here.
jp PrintChar
ReturnToBasic:
RET
StringA:
byte "gHHH5YY++///\",0
- Output:
g, HHH, 5, YY, ++, ///, \
zkl
fcn group(str){
C,out := str[0],Sink(C);
foreach c in (str[1,*]){ out.write(if(c==C) c else String(", ",C=c)) }
out.close();
}
group("gHHH5YY++///\\").println();
- Output:
g, HHH, 5, YY, ++, ///, \
ZX Spectrum Basic
10 LET s$="gHHH5YY++///\"
20 LET c$=s$(1)
30 LET n$=c$
40 FOR i=2 TO LEN s$
50 IF s$(i)<>c$ THEN LET n$=n$+", "
60 LET n$=n$+s$(i)
70 LET c$=s$(i)
80 NEXT i
90 PRINT n$
- Output:
g, HHH, 5, YY, ++, ///, \
- String manipulation
- Strings
- Simple
- Programming Tasks
- Solutions by Programming Task
- 11l
- 8080 Assembly
- 8086 Assembly
- AArch64 Assembly
- Action!
- Ada
- ALGOL 68
- Amazing Hopper
- ANSI BASIC
- APL
- AppleScript
- ARM Assembly
- Arturo
- AutoHotkey
- AWK
- BaCon
- BASIC256
- BBC BASIC
- BQN
- C
- C sharp
- C++
- Clojure
- CLU
- COBOL
- Common Lisp
- Cowgol
- D
- Delphi
- SysUtils,StdCtrls
- Dyalect
- EasyLang
- Ed
- Elixir
- F Sharp
- Factor
- Forth
- Fortran
- FreeBASIC
- Frink
- FutureBasic
- Go
- Haskell
- IS-BASIC
- J
- Java
- JavaScript
- Jq
- Jsish
- Julia
- K
- Kotlin
- Lambdatalk
- Lua
- Ksh
- M2000 Interpreter
- Maple
- Mathematica
- Wolfram Language
- MiniScript
- Modula-2
- Nim
- OoRexx
- Pascal
- Perl
- Phix
- Phixmonti
- PicoLisp
- Pike
- Plain English
- PowerShell
- PureBasic
- Python
- Quackery
- Racket
- Raku
- Refal
- REXX
- Ring
- RPL
- Ruby
- Rust
- Scala
- Sed
- SETL
- Sidef
- SNOBOL4
- Standard ML
- Swift
- Tailspin
- Tbas
- Tcl
- Transd
- Uiua
- VBA
- V (Vlang)
- Wren
- XLISP
- XPL0
- Yabasic
- Z80 Assembly
- Zkl
- ZX Spectrum Basic