Skip to content

Commit

Permalink
Simplify string-join using a loop
Browse files Browse the repository at this point in the history
- The (if (null? result) ...) check can be done once, before the loop.
  It doesn't need to be done separately on each iteration.

- Carrying the result as a string, and using string-append on each
  iteration, is probably more efficient than gathering the strings into
  a temporary list. Avoiding apply will also avoid implementation limits
  on how many args can be passed to a procedure (if list is very long,
  it can exceed the limit).

- Use the names `lst` and `loop` as decided in #16.
  • Loading branch information
lassik committed Jul 7, 2021
1 parent 3dfd70b commit 7dc5b2e
Showing 1 changed file with 7 additions and 9 deletions.
16 changes: 7 additions & 9 deletions recipes/join-list-of-strings-with-delimiter.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,13 @@ SRFI 13 provides the [`string-join` procedure](https://srfi.schemers.org/srfi-13
### Using a loop

```
(define (string-join list delimiter)
(let iter ((list list) (result '()))
(if (null? list)
(apply string-append (reverse result))
(let ((item (car list))
(rest (cdr list)))
(if (null? result)
(iter rest (cons item result))
(iter rest (cons item (cons delimiter result))))))))
(define (string-join lst delimiter)
(if (null? lst) ""
(let loop ((result (car lst)) (lst (cdr lst)))
(if (null? lst)
result
(loop (string-append result delimiter (car lst))
(cdr lst))))))
```

Credit [Jakub T. Jankiewicz](https://jcubic.pl/me)
Expand Down

4 comments on commit 7dc5b2e

@soegaard
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For small lists, it might be the case the "accumulate in strings" version is faster - but it potentially turns an O(n) operations into an O(n^2).
(Consider the special case where the lists consists of strings of length 1).

@lassik
Copy link
Member Author

@lassik lassik commented on 7dc5b2e Jul 8, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Each iteration of the loop will allocate two pairs, which is 2 * 2 words of RAM (32 bytes). At the end we'll reverse them, which makes a copy of the list. Seems unlikely the string-append-only version would allocate significantly more RAM in any scenario, but you may be right :)

@lassik
Copy link
Member Author

@lassik lassik commented on 7dc5b2e Jul 8, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, SRFI 13 has string-concatenate-reverse for this kind of thing. Though an efficient implementation of that procedure will use string ports or some moral equivalent, which will essentially duplicate what our string-join is doing.

@soegaard
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW this was type of loop was discussed here two weeks ago.

https://groups.google.com/g/racket-users/c/ovQ3G_F7BDw/m/JrHn0ftzAgAJ

Please sign in to comment.