[solved] String interpolation

Q&A's, tips, howto's

[solved] String interpolation

Postby vetelko » Sun Jul 30, 2017 3:33 pm

Hello guys,

is there some way how to replace placeholders in string with symbols values?
"value of one is :one: and value of two is :two:"
I want to replace :one: and :two: with values of symbols 'one and 'two if they exists. How to get symbol value if its name is available as string?
Last edited by vetelko on Tue Aug 01, 2017 8:45 am, edited 1 time in total.
newLISP v.10.7.4 64-bit on OpenBSD IPv4/6 UTF-8 libffi
vetelko
 
Posts: 20
Joined: Thu Oct 13, 2016 4:47 pm

Re: String interpolation

Postby tomtoo » Sun Jul 30, 2017 8:49 pm

Something like this?
Code: Select all
> (set (sym "foo") "bar")
"bar"
> foo
"bar"
> (set 'a "one :two: three")
"one :two: three"
> a
"one :two: three"
> (replace ":two:" a foo)
"one bar three"
>
tomtoo
 
Posts: 46
Joined: Wed Oct 28, 2009 10:00 pm

Re: String interpolation

Postby vetelko » Mon Jul 31, 2017 7:01 am

OK. It's like placeholders in function format but named so
instead of %s you are using named placeholders
The effect should be like in PHP where you write something like this
$one = 1;
$two = 2;
$str = "here $one and there $two";

So instead of
(println (format "here %d and there %d" one two))
one can use
(myprintln "here :one: and there :two:")

Code: Select all
(set 'one 1)
(set 'two 2)
(set 'str "here :one: and there :two:")
(set 'placeholders '())
(find-all {:([a-z]+):} str (push $1 placeholders -1))
(dolist (p placeholders)
    ;; pseudo
    replace string p in str with value of symbol of the same name if it exists
    in this case replace word "one" with 1 and word "two" with 2
(println str) # -> here 1 and there 2
newLISP v.10.7.4 64-bit on OpenBSD IPv4/6 UTF-8 libffi
vetelko
 
Posts: 20
Joined: Thu Oct 13, 2016 4:47 pm

Re: String interpolation

Postby vetelko » Tue Aug 01, 2017 8:36 am

I'm new in (new)lisp, if someone can optimize it I would be glad.

Code: Select all
(set 'name "John" 'age 37 'city "NY")

;; P replaces symbol name in string with its value, symbol name is enclosed
;; between colons (or choose your own), symbol must be defined
(define (P str (sep ":"))
    (set 'fields '())
    (find-all (format {%s([a-z0-9-]+)%s} sep sep) str (push $1 fields -1))
    (dolist (f fields)
        (if (set 'val (eval (sym f MAIN nil)))
            (replace (string sep f sep) str (string val))))
    (println str))

;; default call
(P ":name: lives in :city: and is :age: years old.")
;; -> John lives in NY and is 37 years old

;; custom separator
(P "!name! lives in !city! and is !age! years old." "!")
;; John lives in NY and is 37 years old.

;; custom separator, one symbol undefined
(P "~name~ lives in ~city~ and is ~blah~ years old." "~")
;; -> John lives in NY and is ~blah~ years old.

newLISP v.10.7.4 64-bit on OpenBSD IPv4/6 UTF-8 libffi
vetelko
 
Posts: 20
Joined: Thu Oct 13, 2016 4:47 pm

Re: [solved] String interpolation

Postby rickyboy » Tue Aug 01, 2017 6:14 pm

Hi vetelko!

Looks good! You didn't leave us much to work with / optimize. :)

I only have one comment, with doesn't really apply here because you are giving a short, illustrative example. However, in production code, I find it useful to separate the "printing code" from the "(answer) building code". In that light, I looked at your example in a different way, that is, I imagined a different context -- one in which I was looking at a process similar to MS Word's mail merge, where one has a template and then a "database" of "addresses" (or any other grouping of values) with which one wants to fill the template.

So, I wrote something very much like your P function, but without the println.

Code: Select all
(define (fill-template TEMPLATE VALUES)
  (replace ":([a-z]+):"
           TEMPLATE
           (if (lookup (sym $1) VALUES)
               (string $it)
               "<<<value does not exist>>>")
           0))

This function fills in the template TEMPLATE with values from VALUES.

TEMPLATE is a string containing "slots" (to fill in) denoted by symbol names surrounded by the colon character ':', e.g., ":name: is at :place:." (That is, it is in exactly the same format that you proposed.)

VALUES is an alist which associates those symbols (sans the colons) to corresponding values, e.g., '((name "Tom") (place "home")). This part is slightly different from your application, but you will soon see why I chose this below.

So, here is an example call, to get a feel for it.

Code: Select all
> (fill-template ":name: is at :place:." '((name "Tom") (place "home")))
"Tom is at home."

See? Very much like your P function.

Now, here's how the "mail merge"-type application works. First, I have a template. I proudly steal yours. :)

Code: Select all
(define *template* ":name: lives in :city: and is :age: years old.")

Next, I have a database of people.

Code: Select all
(define *people*
  '(((name "John")
     (age  37)
     (city "NY"))
    ((name "Giorgos")
     (age  25)
     (city "Athens"))
    ((name "Elena")
     (age  43)
     (city "Amsterdam"))))

Finally, the "punchline".

Code: Select all
> (map (curry fill-template *template*) *people*)
("John lives in NY and is 37 years old." "Giorgos lives in Athens and is 25 years old."
 "Elena lives in Amsterdam and is 43 years old.")

Or, if you want println.

Code: Select all
> (dolist (p *people*) (println (fill-template *template* p)))
John lives in NY and is 37 years old.
Giorgos lives in Athens and is 25 years old.
Elena lives in Amsterdam and is 43 years old.
"Elena lives in Amsterdam and is 43 years old."

Thanks for sharing your solution! This was a fun break for me today. All the best to you!
(λx. x x) (λx. x x)
rickyboy
 
Posts: 554
Joined: Fri Apr 08, 2005 7:13 pm
Location: Front Royal, Virginia

Re: [solved] String interpolation

Postby vetelko » Tue Aug 01, 2017 7:49 pm

Hello, rickyboy.

Thank you for your long answer. I like the "curry" trick :) I still don't know/use all newlisp goodies. I'm playing with newlisp http server and I wanted to use such a function in my html view templates, this is why function prints by default. Now it looks cleaner for me than println/string combinations.

Code: Select all
<%
(dolist (m lst)
    (p {<div class="member"><b>:m:</b></div>}))
%>


For now, inspired by you, I changed the function definition so you can choose if you want printing or returning. It is solved with additional argument "ret"

Code: Select all
(define (P str (ret nil) (sep ":"))
    (set 'fields '())
    (find-all (format {%s([a-z0-9-]+)%s} sep sep) str (push $1 fields -1))
    (dolist (f fields)
        (if (set 'val (eval (sym f MAIN nil)))
            (replace (string sep f sep) str (string val))))
    (if (not ret)
        (println str)
        str))
newLISP v.10.7.4 64-bit on OpenBSD IPv4/6 UTF-8 libffi
vetelko
 
Posts: 20
Joined: Thu Oct 13, 2016 4:47 pm

Re: [solved] String interpolation

Postby rickyboy » Thu Aug 03, 2017 4:03 am

vetelko wrote:I'm playing with newlisp http server and I wanted to use such a function in my html view templates, this is why function prints by default. Now it looks cleaner for me than println/string combinations.

Code: Select all
<%
(dolist (m lst)
    (p {<div class="member"><b>:m:</b></div>}))
%>

Ah, I see what you are doing. Yes, you are right: make it simple and just print it.

In that case, I'd go with the definition of `p` as the following.

Code: Select all
(define (p str (sep ":"))
  (println
   (replace (string sep "([a-z]+)" sep)
            str
            (string (eval (sym $1)))
            0)))

Or, with the `ret` flag back in.

Code: Select all
(define (p str (ret nil) (sep ":"))
  ((if ret begin println)
   (replace (string sep "([a-z]+)" sep)
            str
            (string (eval (sym $1)))
            0)))

I'm just playing around here -- having fun. Really, you did an excellent job in the first place. Cheers!
(λx. x x) (λx. x x)
rickyboy
 
Posts: 554
Joined: Fri Apr 08, 2005 7:13 pm
Location: Front Royal, Virginia

Re: [solved] String interpolation

Postby vetelko » Fri Aug 04, 2017 5:51 pm

Thanks for your version rickyboy. Things happen when I don't know the language enough. Knowing replace can use regexp one can avoid find-all :) Cool.
newLISP v.10.7.4 64-bit on OpenBSD IPv4/6 UTF-8 libffi
vetelko
 
Posts: 20
Joined: Thu Oct 13, 2016 4:47 pm

Re: [solved] String interpolation

Postby varbanov » Fri Aug 18, 2017 11:00 am

Hi guys,

Sorry for posting on a solved task, but I just can't resist temptation to show my context version. :) No regular expresions, no (explicit) search/replace. :)
Code: Select all
(define *people*
      '((("name" "John")
         ("age"  37)
         ("city" "NY"))
        (("name" "Giorgos")
         ("age"  25)
         ("city" "Athens"))
        (("name" "Elena")
         ("age"  43)
         ("city" "Amsterdam"))))

(define *template* ":name: lives in :city: and is :age: years old.")

(setq parsed-template (parse *template* ":"))

(define data:data)     ; defines context to hold one person data

(dolist (p *people*)
   (data p)
   (dolist (str parsed-template)
      (print (or (data str) str)))
   (println))


Yours,
s.v.
varbanov
 
Posts: 6
Joined: Mon Jul 01, 2013 1:33 pm
Location: Sofia, Bulgaria


Return to newLISP in the real world

Who is online

Users browsing this forum: No registered users and 2 guests