Advice on programming style...

Started by cormullion, October 11, 2007, 09:49:54 AM

Previous topic - Next topic

cormullion

If you want to process some data using a series of functions, then one way to do it is like this:


(set 's "string")
(func1)
(func2)
(func3)


where func1 and func1 just do things to s - s being global...



But you could also do it like this:


(set 's "string")
(set 's (func1 s)
(set 's (func2 s)
(set 's (func3 s)


where func1 and co return a modified version of s. This is presumably less efficient (since s is being shuffled around and copied...)? It doesn't look so good - repetition, too many set's.



There's also


(set 's (func3 (func2 (func1 s)))

where func1 are as the previous example. I don't like this because it's backwards.



What I want to write is:


(*?* func1 func2 func3 s)


or more likely:


(*?* '(func1 func2 func3) s)


I can't see how to do it... It's probably got a name...?

Jeff

#1
Using globals for state is generally bad practice, especially with dynamic functions, because you can never completely accurately predict the value of the global when the function is called.



The lispy solution is (func3 (func2 (func1 s))).  Realistically, the best alternative is to hide that in a func4 in order to make the code setting the value of s more expressive.



The lispy alternative is to create a function that serially applies functions to the same value, like:


(define (s-apply fn-list val)
  (dolist (f fn-list)
    (setq val (f val))
  val)
Jeff

=====

Old programmers don\'t die. They just parse on...



http://artfulcode.net\">Artful code

jrh

#2
That dolist looks like Fortran (as do all dolists).



(func3 (func2 (func1 s))) is LISP !!!

-

cormullion

#3
Hi Jeff. Yes, that works well.


(define (s-apply fn-list val)
  (dolist (f fn-list)
    (set 'val (f val)))
   val)

(println (s-apply (list reverse upper-case explode) "hi jeff"))

;-> ("F" "F" "E" "J" " " "I" "H")


I suppose it's kind of opposite to map - mapping lots of functions onto a single argument, rather than one function onto lots of arguments.



Thanks.

jrh

#4
Subroutines instead of functions, whatever floats your boat, but I'll pass.

rickyboy

#5
Remember that for expressions like
(f (g (h x)))
you can always use a function composer, e.g. some are found in http://newlisp-on-noodles.org/wiki/index.php/Functional_Programming#Composing_functions">//http://newlisp-on-noodles.org/wiki/index.php/Functional_Programming#Composing_functions and write instead
((compose f g h) x)
I think it's more natural to write the function constituents right-to-left (like the mathematical convention of functional composition), but you could write another composer that took the functions in reverse order.  I'd call it rcompose, so as not to confuse it with the conventional composer (sorry cormullion, you may be in the minority):
((rcompose h g f) x)

Also, I agree with jrh.  I'd rather write a composer (in general, these are called combinators) which generates a new function than write a function that just applies its constituents right away.  (Also, I think dolist is ugly too, but it is a faster iterator, which I believe was Jeff's chief concern.)  Utilizing a combinator leaves the door open for building computations and keeping them in symbol references for continued reuse. For instance, you can't use s-apply (defined above) in a fold operation, so the composer is more flexible:
(let ((my-operator (compose more-func your-operator)))
  (fold my-operator '(1 2 3 45 49 20 98 892)))

That is, now you can chose not only when but also how to apply the new function.
(λx. x x) (λx. x x)

cormullion

#6
Excellent! Thanks.



Once I know what things are called, it's easier to find out about them...

Jeff

#7
Ricky,



That sounds an awful lot like continuations.  Is that what combinators are?



Jeff
Jeff

=====

Old programmers don\'t die. They just parse on...



http://artfulcode.net\">Artful code