puzzle block created?*probe puzzle == [3 + 2 2 * 3 3 - 2 2 - 3] reduce puzzle == [First Five then Six and then One and finally Minus One. Ta-da!] reduce load mold puzzle == [5 6 1 -1]
load, mold, probe, or reduce; and no Unicode trickery?*puzzle block are simply Red words. "+" and "-" turn into word with to-word. To make digits into words we ask load nicely.reduced:lsw: does [last words-of system/words] hint: copy [] foreach [w n] [1 2 -3 -6 5x25 10x50 3.14159 tau][load rejoin ["/" w] set lsw n append hint lsw] probe hint == [1 -3 5x25 3.14159] ;; four words reduce hint == [2 -6 10x50 tau] ;; and the values they hold
>> /2 == /2 >> last words-of system/words == 2 >> type? last words-of system/words == word!
>> prepare puzzle: [3 + 2 2 * 3 3 - 2 2 - 3] == [3 + 2 2 * 3 3 - 2 2 - 3] >> last reduce puzzle == [First Five then Six and then One and finally Minus One. Ta-da!] >> reduce load mold puzzle == [5 6 1 -1] >> prepare puzzle: [3 + 4 2 * 3 5 - 2 1 - 3] == [3 + 4 2 * 3 5 - 2 1 - 3] >> last reduce puzzle == [First Seven then Six and then Three and finally Minus Two. Ta-da!] >> reduce load mold puzzle == [7 6 3 -2] ...
>> prepare puzzle: [1 + 3 2 - 5 6 + 2 5 - 1 12 - 3 -3 + 4] == [1 + 3 2 - 5 6 + 2 5 - 1 12 - 3 -3 + 4] >> do puzzle == [First Four then Minus Three and then Eight and then Four and then Nine and finally One. Ta-da!] >> reduce load mold puzzle == [4 -3 8 4 9 1]
binary! that is 16 bytes long. The use case is generating UUIDs, where you start with 16 fully random bytes and then set a few bits at specific locations. I've tinkered on about 20 variations over time.fn-p: function [][
data: copy #{00000000000000000000000000000000}
repeat i 16 [data/:i: random/secure 256]
data
]copying isn't an option, as it just moves that burden to the user). In my variations, one approach produced results like #{43DFD61C4E82F7E743D13859B5DFF62B}
#{43DE1CA89626A15243DA8CF5BB420739}
#{43C0BF74C7F2BC4843C09E7658C047AC}
#{43BE565180CDFC6143C2B46C8152C7FD}
#{43D87D5DD8E33B8F43DD270591B9F989}>> to binary! map-each c 16 [-1 + random 256]
== #{CA0FE066E464D5A83642FA894846A720}>> recycle/off prof/each/times [fn-p append append make #{} 16 random 255.255.255.255.255.255.255.255 random 255.255.255.255.255.255.255.255] 100000
<100000>84% .0116 ms 44 B [fn-p]
<100000>16% .0022 ms 132 B [append append make #{} 16 random 2...]>> copy/part checksum form now/precise 'sha256 16 │
== #{80F3A47BEAECD902395728F63FC16032}-1 +, but hit that in some of mine and depended on Red's moduloing bytes in binaries to avoid it. now/precise for random seeding, but can we trust it to not return the same result on all platforms, if run in a tight loop? I like the leverage though. Could also clear instead of copy, yes?random/secure may not be all that secure... though it looks a lot different when I try it hererecord-manager idea is basically data in blocks and sorted blocks of indexes.Count: 100000 Time | Time (Per) | Memory | Code 0:00:00.148 | 0:00:00 | 13'200'440 | fn-v 0:00:00.356 | 0:00:00 | 8'800'840 | fn-o 0:00:00.36 | 0:00:00 | 13'200'840 | fn-t 0:00:00.423 | 0:00:00 | 13'200'840 | fn-r 0:00:00.44 | 0:00:00 | 13'200'840 | fn-q 0:00:00.581 | 0:00:00 | 25'201'072 | fn-u 0:00:00.714 | 0:00:00 | 35203172 | fn-a 0:00:00.843 | 0:00:00 | 17600872 | fn-d 0:00:00.891 | 0:00:00 | 17601056 | fn-e 0:00:00.948 | 0:00:00 | 4400840 | fn-s 0:00:00.964 | 0:00:00 | 4400640 | fn-p 0:00:01.205 | 0:00:00 | 4400640 | fn-b 0:00:01.42 | 0:00:00 | 1152 | fn-c ; no copy, so unsafe 0:00:01.536 | 0:00:00 | 8800920 | fn-g 0:00:01.566 | 0:00:00 | 17600920 | fn-f
fn-p function already. Would it make sense to add an /into refinement to random to request a specific amount of bytes?fn-u.fn-v, Boris' tuple version to the table above. Funky as it appears, I am not surprised that it rose to the top of the pile. Nice one @hiiamboris.>> prepare puzzle: [5 + 6 3 - 3 2 * 9 211 * 34] == [5 + 6 3 - 3 2 * 9 211 * 34] >> do puzzle == [First Eleven then Zero and then Eighteen and finally Seven Thousand One Hundred Seventy-Four. Ta-da!] >> reduce load mold puzzle == [11 0 18 7174]
/secure with random. The question, then, is how important that is for UUID use.random alone be enough for the UUID case. More of a thought experiment for general use, as what we have how should still be fast enough.print dedupe "nNNnennnnaaadd" == nNnenad print mold dedupe ["N" "N" "e" "n" "n" "a" "a" "d" "d" "d"] == ["N" "e" "n" "a" "d"]
dedupe: func [s-in [series!] ][ s-in: copy s-in ; so we don't modify caller's series directly forall s-in [while [s-in/1 = s-in/2] [remove s-in]] return s-in ]
dedupe-test: does [
random/seed 1 ; for repeatable random data
dedupe-test-data: make string! 1000000
loop 1000000 [append dedupe-test-data random/only "AEIOU"]
print dt[dedupe-test-result: dedupe dedupe-test-data]
print ["Input length:" length? dedupe-test-data "Output length:" length? dedupe-test-result]
]
loop 5 [dedupe-test]dedupe: func [series [series!]][parse/case out: copy series [any [set _ skip any [ahead _ remove skip]]] out]
>> print dedupe "nNNnennnnaaadd" nNnenad >> print mold dedupe ["N" "N" "e" "n" "n" "a" "a" "d" "d" "d"] ["N" "e" "n" "a" "d"]
>> loop 5 [dedupe-test] 0:00:03.40687 Input length: 1000000 Output length: 800153 0:00:03.4109 Input length: 1000000 Output length: 800153 0:00:03.41485 Input length: 1000000 Output length: 800153 0:00:03.39291 Input length: 1000000 Output length: 800153 0:00:03.45179 Input length: 1000000 Output length: 800153
dedupe: func [series [series!]][parse/case out: copy series [any [set _ skip remove any _]] out] loop 5 [dedupe-test] 0:00:02.61498 Input length: 1000000 Output length: 800153 0:00:02.67284 Input length: 1000000 Output length: 800153 0:00:02.65391 Input length: 1000000 Output length: 800153 0:00:02.65889 Input length: 1000000 Output length: 800153 0:00:02.66093 Input length: 1000000 Output length: 800153
dedupe: function [s][also t: clear copy s parse s [collect into t any [keep set x skip any x]]]/local x needed :)/case too>> loop 5 [dedupe-test] 0:00:00.597404 Input length: 1000000 Output length: 800153 0:00:00.60142 Input length: 1000000 Output length: 800153 0:00:00.664142 Input length: 1000000 Output length: 800153 0:00:00.596381 Input length: 1000000 Output length: 800153 0:00:00.620573 Input length: 1000000 Output length: 800153
>> dedupe: function [s /local x][replace/all/deep/case copy s [set x skip any x] does [x]] >> print dedupe "nNNnennnnaaadd" nNnenad >> print mold dedupe ["N" "N" "e" "n" "n" "a" "a" "d" "d" "d"] ["N" "e" "n" "a" "d"]
>> dedupe: function [s /local x][mapparse/case [set x skip any x] s [x]] >> print dedupe "nNNnennnnaaadd" nNnenad >> print mold dedupe ["N" "N" "e" "n" "n" "a" "a" "d" "d" "d"] ["N" "e" "n" "a" "d"]
change bug that makes my "fun" solutions really slow on big series. mapparse way is still my favorite though.dedupe: function [s /local x][mapparse/case [set x skip any x] copy s [x]] is the proper solution though (mapparse doesn't copy right now)dedupe: function [s][ res: make string! length? s last-seen: none foreach ch s [ if ch <> last-seen [append res ch] last-seen: ch ] res ]
driver 50 5000 ; run each contender 50 times with a 5000 long series driver 5 1000000 ; five iterations with a 1 million long series
ser: make string! len loop len [append ser random/only "AEIOU"]
Red []
contenders: reduce [
"Sunanda"
func [
s-in [series!]
][
s-in: copy s-in ; so we don't modify caller's series directly
forall s-in [while [s-in/1 = s-in/2] [remove s-in]]
return s-in
]
"Toomas"
func [series [series!]] [parse/case out: copy series [any [set _ skip [remove any _]]] out]
"Boris"
func [s /local x] [also t: clear copy s parse/case s [collect into t any [keep set x skip any x]]]
"Greg"
function [s][
res: make string! length? s
last-seen: none
foreach ch s [
if ch <> last-seen [append res ch]
last-seen: ch
]
res
]
] ;; contenders
Driver: func [reps [integer!] len [integer!] /local ser res resb nam fun tim tims tot] [
ser: make string! len
loop len [append ser random/only "AEIOU"]
resb: copy []
tims: copy []
foreach [nam fun] contenders [
recycle
tot: 0:0:0
loop reps [
tim: now/time/precise
res: do [do [fun ser]]
tot: tot + now/time/precise - tim
append resb res
]
append tims reduce [tot nam]
]
if 1 <> length? unique resb [print "Not identical results!"]
sort/skip tims 2
print newline
print ["Input length:" len "Output length:" length? resb/1 "Reps:" reps]
foreach [tim nam] tims [print [" " pad/left/with tim 12 #"0" ": " nam]]
return tims
]