Page 1 of 3

interleave/join strings

Posted: Sat Jan 14, 2017 6:27 pm
by alexeyv
Hi,

What is the best way to join strings? For example given an array of strings, how to create one string with spaces between them?

I came to this solution:

Code: Select all

S←'this' 'is' 'a' 'sentence'
¯1↓∊(,[1.5]S),' '
┌→─────────────────┐
│this is a sentence│
└──────────────────┘


Is there more elegant way I can't see ?

Re: interleave/join strings

Posted: Sat Jan 14, 2017 7:09 pm
by Veli-Matti
Hi,
the solutions for this specific purpose I have been using lately are either
      1↓↑,/,' ',⍪S

or
      1↓↑,/,' ',⍤0⊢S


-Veli-Matti

Re: interleave/join strings

Posted: Sat Jan 14, 2017 8:12 pm
by Veli-Matti
For your amusement: testing several approaches with cmpx when using single character separator and a little bit longer nested vector:
      D←' ' ⋄ S←12345⍴S
D{1↓↑,/,⍺,⍤0⊢⍵}S → 1.0E¯3 | 0% ⎕⎕⎕⎕⎕⎕
D{1↓↑,/,⍺(,⍤0)⍵}S → 1.0E¯3 | +3% ⎕⎕⎕⎕⎕⎕⎕
D{1↓↑,/,⍺∘,⍤0⊢⍵}S → 5.3E¯3 | +427% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
D{1↓∊⍺∘,⍤0⊢⍵}S → 6.3E¯3 | +522% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
D{1↓↑,/,⍺,⍪⍵}S → 1.1E¯3 | +9% ⎕⎕⎕⎕⎕⎕⎕
D{1↓∊⍺,¨⍵}S → 2.8E¯3 | +176% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
D{¯1↓↑,/,⍵,[1.1]⍺}S → 1.0E¯3 | 0% ⎕⎕⎕⎕⎕⎕
D{¯1↓∊(,[1.5]⍵),⍺}S → 2.1E¯3 | +109% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕

and with a vector separator:
      D←'","'
D{(⍴⍺)↓↑,/,(⊂⍺),⍤0⊢⍵}S → 1.1E¯3 | 0% ⎕⎕⎕⎕⎕
D{(≢⍺)↓↑,/,(⊂⍺)(,⍤0)⍵}S → 1.2E¯3 | +8% ⎕⎕⎕⎕⎕
D{(⍴⍺)↓↑,/,⍺∘,⍤0⊢⍵}S → 7.0E¯3 | +557% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
D{(⍴⍺)↓∊⍺∘,⍤0⊢⍵}S → 9.1E¯3 | +751% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
D{(⍴⍺)↓↑,/,(⊂⍺),⍪⍵}S → 1.1E¯3 | +5% ⎕⎕⎕⎕⎕
D{(⍴⍺)↓∊(⊂⍺),¨⍵}S → 2.5E¯3 | +134% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
D{(-⍴⍺)↓↑,/,⍵,[1.1]⊂⍺}S → 1.1E¯3 | 0% ⎕⎕⎕⎕⎕
D{(-⍴⍺)↓∊(,[1.5]⍵),⊂⍺}S → 2.1E¯3 | +97% ⎕⎕⎕⎕⎕⎕⎕⎕⎕


There are remarkable differences with just small code changes.

-Veli-Matti

Re: interleave/join strings

Posted: Sat Jan 14, 2017 9:56 pm
by petermsiegel
Interesting.
I expected {1↓⊃,/,⍺,⍤0⊢⍵} to slightly exceed the performance of {1↓↑,/,⍺,⍤0⊢⍵}, your fastest, on my Macbook Pro, but it ran identically in my tests. I always used {1↓∊⍺,¨⍵}, since it "appears" to me to be algorithmically the simplest, but, given the performance numbers, perhaps not.

Re: interleave/join strings

Posted: Sat Jan 14, 2017 11:24 pm
by Phil Last
Not surprising that those using enlist are significantly slower than their counterparts using catenate reduction. The former was always much slower from the outset while the latter has had a number of speed-ups over the years.

I've never encountered a single situation where enlist even suggests itself as being a good solution given that we almost always know the structure of the data we're dealing with.

A sledgehammer of a function if ever there was one.

As to simplifying a list of strings I've always found
      1↓⊃,/⍺∘,¨⍵
to be the best and was surprised not to see it in Veli-Matti's list.

Re: interleave/join strings

Posted: Sun Jan 15, 2017 1:23 am
by petermsiegel
Enlist ∊ on the left seems to add an 8-11% slowdown compared with catenate-based code (depending on what's to its right), so other parts of the code (the first/right-most catenation) in Veli-Matti's examples dominate the results. As always, real performance may depend on bigger fish.

Re: interleave/join strings

Posted: Sun Jan 15, 2017 10:35 am
by Veli-Matti
There are so many factors when deciding the 'best' idiom for any task. If you know beforehand that the argument is nested vector, the
      ↑,/
idiom is the fastest way around (that is, if Roger hasn't invented something under the bonnet lately..).

Just for amusement I run some tests (v15/Unicode, 32-bit, Win10).
First, pure idioms with scalar separator (' ') and short argument ('this' 'is' 'a' 'sentence'):

      1↓↑,/,(⊂D),⍤0⊢V  → 1.1E¯6 |    0% ⎕⎕⎕⎕⎕⎕                                  
1↓↑,/,(⊂D)(,⍤0)V → 1.1E¯6 | +4% ⎕⎕⎕⎕⎕⎕⎕
¯1↓↑,/,V,[1.1]⊂D → 1.0E¯6 | -4% ⎕⎕⎕⎕⎕⎕
1↓↑,/,(⊂D),⍪V → 1.0E¯6 | -6% ⎕⎕⎕⎕⎕⎕
1↓↑,/,D∘,⍤0⊢V → 6.5E¯6 | +510% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
1↓∊D∘,⍤0⊢V → 6.8E¯6 | +541% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
1↓↑,/D∘,¨V → 1.7E¯6 | +56% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
1↓∊(⊂D),¨V → 1.7E¯6 | +62% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
¯1↓∊(,[1.5]V),⊂D → 1.8E¯6 | +70% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
↑{⍺,D,⍵}/V → 2.1E¯6 | +92% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕


Next test with the same arguments, but the sentences in dfns
      D{1↓↑,/,(⊂⍺),⍤0⊢⍵}V  → 1.4E¯6 |    0% ⎕⎕⎕⎕⎕⎕⎕                                 
D{1↓↑,/,(⊂⍺)(,⍤0)⍵}V → 1.4E¯6 | +4% ⎕⎕⎕⎕⎕⎕⎕⎕
D{¯1↓↑,/,⍵,[1.1]⊂⍺}V → 1.3E¯6 | -2% ⎕⎕⎕⎕⎕⎕⎕
D{1↓↑,/,(⊂⍺),⍪⍵}V → 1.3E¯6 | -7% ⎕⎕⎕⎕⎕⎕⎕
D{1↓↑,/,⍺∘,⍤0⊢⍵}V → 6.8E¯6 | +398% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
D{1↓∊⍺∘,⍤0⊢⍵}V → 7.3E¯6 | +435% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
D{1↓↑,/⍺∘,¨⍵}V → 1.9E¯6 | +42% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
D{1↓∊(⊂⍺),¨⍵}V → 2.0E¯6 | +47% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
D{¯1↓∊(,[1.5]⍵),⊂⍺}V → 2.0E¯6 | +48% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
{↑{⍺,D,⍵}/⍵}V → 2.3E¯6 | +70% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕


Then scalar separator with a longish argument (12345<reshape>'this' 'is' 'a' 'sentence')

      D{1↓↑,/,(⊂⍺),⍤0⊢⍵}V  → 1.0E¯3 |     0%                                         
D{1↓↑,/,(⊂⍺)(,⍤0)⍵}V → 1.0E¯3 | 0%
D{¯1↓↑,/,⍵,[1.1]⊂⍺}V → 1.0E¯3 | +1%
D{1↓↑,/,(⊂⍺),⍪⍵}V → 1.1E¯3 | +6%
D{1↓↑,/,⍺∘,⍤0⊢⍵}V → 5.3E¯3 | +419% ⎕⎕
D{1↓∊⍺∘,⍤0⊢⍵}V → 6.3E¯3 | +513% ⎕⎕⎕
D{1↓↑,/⍺∘,¨⍵}V → 3.2E¯3 | +208% ⎕
D{1↓∊(⊂⍺),¨⍵}V → 2.8E¯3 | +171% ⎕
D{¯1↓∊(,[1.5]⍵),⊂⍺}V → 2.1E¯3 | +105% ⎕
{↑{⍺,D,⍵}/⍵}V → 9.5E¯2 | +9187% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕


Last, vector separator ('","') with long argument
      D{(⍴⍺)↓↑,/,(⊂⍺),⍤0⊢⍵}S  → 1.0E¯3 |      0%                                         
D{(≢⍺)↓↑,/,(⊂⍺)(,⍤0)⍵}S → 1.0E¯3 | -2%
D{(-⍴⍺)↓↑,/,⍵,[1.1]⊂⍺}S → 1.0E¯3 | -2%
D{(⍴⍺)↓↑,/,(⊂⍺),⍪⍵}S → 1.1E¯3 | +5%
D{(⍴⍺)↓↑,/⍺∘,¨⍵}S → 2.8E¯3 | +171% ⎕
D{(⍴⍺)↓∊(⊂⍺),¨⍵}S → 2.5E¯3 | +143% ⎕
D{(⍴⍺)↓↑,/,⍺∘,⍤0⊢⍵}S → 7.1E¯3 | +577% ⎕⎕
D{(⍴⍺)↓∊⍺∘,⍤0⊢⍵}S → 9.0E¯3 | +758% ⎕⎕⎕
D{(-⍴⍺)↓∊(,[1.5]⍵),⊂⍺}S → 2.1E¯3 | +100% ⎕
{↑{⍺,D,⍵}/⍵}S → 1.3E¯1 | +12525% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕


-Veli-Matti

PS. Phil, I didn't forget that idiom, I just feel that composition is as avoidable as enlist :)

Re: interleave/join strings

Posted: Sun Jan 15, 2017 10:38 am
by Morten|Dyalog
OK, looks like we (Dyalog) have some work to do. Thanks for pointing this out!

Code: Select all

      5↑txt←(?100⍴10)⍴¨⊂⎕A
 ABCDEFGH  ABCDEFG  ABCDEFG  A  ABCDEF
      cmpx '⊃,/'' '',¨txt' '∊'' '',¨txt' '{n←1+≢¨⍵ ⋄ (,n∘.≥⍳⌈/n)/,'' '',↑⍵}txt'
  ⊃,/' ',¨txt                        → 1.9E¯5 |   0% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕   
  ∊' ',¨txt                          → 2.1E¯5 | +12% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
  {n←1+≢¨⍵ ⋄ (,n∘.≥⍳⌈/n)/,' ',↑⍵}txt  → 7.1E¯6 | -63% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕                           

Re: interleave/join strings

Posted: Sun Jan 15, 2017 11:12 am
by Veli-Matti
..running Morten's charming vintage idiom with the same testbed as the earlier ones (with the modern []ML :) ):
      ' ' ∆tst0 12345⍴'this' 'is' 'a' 'sentence'  
¯1↓↑,/,V,[1.1]⊂D → 9.8E¯4 | 0%
1↓↑,/,(⊂D),⍤0⊢V → 9.8E¯4 | 0%
1↓↑,/,(⊂D)(,⍤0)V → 1.1E¯3 | +12%
1↓↑,/,(⊂D),⍪V → 1.1E¯3 | +12%
¯1↓∊(,[1.5]V),⊂D → 2.1E¯3 | +112% ⎕
1↓(,n∘.≥⍳⌈/n←1+≢¨V)/,D,⊃V → 2.6E¯3 | +163% ⎕
1↓∊(⊂D),¨V → 2.7E¯3 | +174% ⎕
1↓↑,/D∘,¨V → 3.2E¯3 | +225% ⎕
1↓↑,/,D∘,⍤0⊢V → 5.1E¯3 | +424% ⎕⎕
1↓∊D∘,⍤0⊢V → 6.1E¯3 | +524% ⎕⎕⎕
↑{⍺,D,⍵}/V → 9.2E¯2 | +9363% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕


-wm

Re: interleave/join strings

Posted: Sun Jan 15, 2017 11:18 am
by Veli-Matti
Ouch! There was some unnecessary discloses included. When they are removed, the rank idioms seem to be the quickest ones:
      1↓↑,/,D,⍤0⊢V              → 9.8E¯4 |     0%                                         
1↓↑,/,D(,⍤0)V → 1.1E¯3 | +12%
¯1↓↑,/,V,[1.1]D → 1.6E¯3 | +62% ⎕
1↓↑,/,D,⍪V → 1.2E¯3 | +24% ⎕
etc..

-wm