About Standards and Best Practices
[1/11] from: reboler::bol::com::br at: 1-Sep-2003 7:07
Hi Folks!,
Sorry if it's a dummy question, but I was wondering about saving system
resources in Rebol.
What do you suggest as the best practices?
1. Always use objects?
2. Functions encapsulated in Objects with local vars ?
3. use clear and unset to release the vars?
Or I don't need to create the objects and what I need is just to create the
functions with local vars to save memory, then clear them..
Have you already written about this? I mean with the focus on Rebol. I think
if we have something like this, we would have more improvement to coding.
I read a message of Andrew optimizing his code, I think a manual how to
optimize a script with standards and best practices would be great!. Don't
you think?
Best Regards
Thanks in advance.
-Dj
[2/11] from: greggirwin:mindspring at: 1-Sep-2003 17:37
Hi Dj,
R> Sorry if it's a dummy question, but I was wondering about saving system
R> resources in Rebol.
R> What do you suggest as the best practices?
R> 1. Always use objects?
R> 2. Functions encapsulated in Objects with local vars ?
R> 3. use clear and unset to release the vars?
R> Or I don't need to create the objects and what I need is just to create the
R> functions with local vars to save memory, then clear them..
It's not a dumb question, but I haven't seen too much discussion about
it either. The only time really severe resource usage occurs is when
you have *lots* of data (e.g. tens of thousands of items in blocks) or
when you have a View app that creates a large number of faces
dynamically. One of the nice things about REBOL is really *not*
worrying about those details most of the time. :)
Do you have a specific situation you're dealing with?
-- Gregg
[3/11] from: reboler:bol at: 1-Sep-2003 22:54
Hi Gregg,
I know that Rebol is cool!!, but what I was thinking is that any programmer
has his view of Rebol. Some of them use a lot of objects, others use just
function calls. By the other hand, Rebol is so handy that we can also use a
procedural approach. But what are you missing here? the performance!!
In my point of view, we can make the script to run. Then, make it run faster
and faster!!. Do you know what I mean ?
I saw a lot of documentation on style scripting, functions and commands, but
not for optimization such as: global vars, local vars, encapsulation,
substitution of functions by other that can handle better than another..
etc. We have this information disperse on the list. In most of times, We
need to evaluate other's coding to learn with the Gurus.. do you know what I
mean ?
What I'm asking is just a few tips such as:
1) create objects when ....
2) always use local vars inside functions. (or if this var is inside a
function, it's a local var anyway).
3) call various scripts or code everything in one file..
4) clear, reset vars..
5) use dynamic vars inside functions..
6) work creating instances of objects
I know, it's very dummy, but it helps people.. you know.
Best Regards
Thans in advance
--DJ
[4/11] from: brett:codeconscious at: 2-Sep-2003 12:38
> What I'm asking is just a few tips such as:
> 1) create objects when ....
<<quoted lines omitted: 4>>
> 5) use dynamic vars inside functions..
> 6) work creating instances of objects
Very good question. I have struggled with coming up with my own style that
takes most advantage of REBOL's flexibility. The thing that I have found is
that REBOL's flexibility actually makes it hard to create rules like "Always
....". I invariably find another situation that makes the very thing I'm
trying to avoid useful or innovative. So it turns on the situation.
It is probably best then to have a place where these "idioms" can be
proposed and discussed. Each should establish a situation and a possible
idiom that would be useful. Like another cookbook - but aimed at someone who
has already learnt the basics of REBOL. This is similar in concept to the
early days of "design patterns" - but I would suggest trying to avoid the
dogmatism that I saw in that area of work when I last looked. The risk is if
you go into too much detail (it must be this way or that) then you lose the
value of the idea, I think.
The existing REBOL code produced by RT is implicitly a guide too.
Regards,
Brett
[5/11] from: andrew:martin:colenso:school at: 2-Sep-2003 14:56
> What I'm asking is just a few tips such as:
> 1) create objects when ....
Use objects when the names in the script look something like this:
Bla_foo: ; Rebol value here.
Bla_bar: ; Rebol value here.
Bla_thing: ; Rebol value here.
Turn the above into:
Bla: make object! [
foo: ; Rebol value here.
bar: ; Rebol value here.
thing: ; Rebol value here.
]
Why? An object keeps things together that work together and makes it
obvious.
> 3) call various scripts or code everything in one file...
Or the third option, put one's common functions into files of their own
making a library of files/script/values, then assemble them
automatically into a larger script/file for distribution.
Why? Minimises redundancy, and there's only source. If a mistake is
found in a library file, one only needs to correct it one place, then
reassemble any distributions that rely on that script/library (or even
just re-assemble them all if not lazy).
Just my opinion.
Andrew J Martin
Attendance Officer &
Information Systems Trouble Shooter
Colenso High School
Arnold Street, Napier.
Tel: 64-6-8310180 ext 826
Fax: 64-6-8336759
http://colenso.net/scripts/Wiki.r?AJM
http://www.colenso.school.nz/
DISCLAIMER: Colenso High School and its Board of Trustees is not responsible (or legally
liable) for materials distributed to or acquired from user e-mail accounts. You can report
any
misuse of an e-mail account to our ICT Manager and the complaint will be investigated.
(Misuse can come in many forms, but can be viewed as any material sent/received that
indicate or suggest pornography, unethical or illegal solicitation, racism, sexism, inappropriate
language and/or other issues described in our Acceptable Use Policy.)
All outgoing messages are certified virus-free by McAfee GroupShield Exchange 5.10.285.0
Phone: +64 6 843 5095 or Fax: +64 6 833 6759 or E-mail: [postmaster--colenso--school--nz]
[6/11] from: greggirwin:mindspring at: 2-Sep-2003 9:18
Hi Reboler,
R> I know that Rebol is cool!!, but what I was thinking is that any programmer
R> has his view of Rebol. Some of them use a lot of objects, others use just
R> function calls. By the other hand, Rebol is so handy that we can also use a
R> procedural approach. But what are you missing here? the performance!!
Your questions aren't dumb at all, just hard to answer. :) The problem
is that, with REBOL, you can do things in so many different ways that
it's hard to define "general" optimizations, as Brett said. Sometimes
rethinking the solution in an entirely different way--maybe not
possible in other languages--will yield a simpler and much more
efficient implementation. That said, there are some obvious, and
not-so-obvious rules you can follow.
1) Premature optimization is a big problem is software development.
Only optimize things you *know* will make a significant difference. To
find those things, you have to profile and measure. I profile things
in different ways, but here's a little function I use to test isolated
blocks of code.
time-it: func [block /count ct /local t] [
if not count [ct: 1]
t: now/time/precise
loop ct [do block]
now/time/precise - t
]
print time-it [] 1'000'000
i: 0
print time-it [i: i + 1] 1'000'000
fn: func [/ref val][]
print time-it/count [fn] 1'000'000
print time-it/count [fn/ref none] 1'000'000
print time-it/count [make object! []] 1'000'000
print time-it/count [
make object! [a: none b: "test" c: copy []]
] 1'000'000
2) Optimize algorithms before you optimize code. In REBOL, we're
already working at a high level, but this can also mean using the
right data structure or datatype. For example, list! values are faster
than blocks if you are inserting and removing a lot of items, and
hash! values are much faster than blocks if you're doing a lot of
lookups (i.e. finding or selecting items).
3) Function calls take time. You will sometimes see people inline the
source of the APPEND function (which is basically INSERT TAIL) to save
a little time. If you have a loop that will make lots of calls, it can
*sometimes* be worth eliminating function calls this way, but remember
that it can also lead to more bugs and more code.
4) Path evaluation takes time. REBOL is interpreted, so sometimes it
can pay to cache values that are part of a deep path (in nested blocks
for example) if you're going to use them many times inside a loop.
This applies to functions inside objects as well.
5) For dynamic code, build blocks rather than strings if you're going
to DO them. It can be much easier, cleaner, and more efficient.
6) Use natives when you can. REBOL's natives are very fast. Rather
than looping over a series yourself, looking for values, try to figure
out a way to use natives like FIND and SELECT. Let REBOL do all the
heavy lifting when you can.
6.1) Get to know how some mezzanines work and which functions are
native. The FOR function is a good example. I see a lot of code that
uses FOR with a lower bound of 1 and a step size of 1. In those cases,
REPEAT is much more efficient. Loop constructs in REBOL are not all
native, and certainly not all equal in how they work, so they're worth
investigating.
7) If you're processing text, don't forget about PARSE (even if you
think it's not applicable to your situation).
R> 4) clear, reset vars..
This can make a substantial difference in some cases. If you're
creating buffers to use over and over again, particularly large ones,
rather than doing a "make string! 1'000'000" on every pass, do it once
and then just CLEAR the string on each pass.
For me, the big gains come when I can refactor a solution. Often times
stripping out my own code when I find a native alternative will be all
it takes.
HTH!
-- Gregg
[7/11] from: rebol:techscribe at: 2-Sep-2003 9:06
To flesh out Andrew's reply a little:
1. Use objects if you want to process assemblies of values (records)
that share the same structure uniformly, even though at times some of
the values may be missing. The prototype object ensures that all data
instances will have a complete set of fields that are initialized with
some useful value (in our example we'll be generating reports, and
therefore if a record is missing some data, we will want to display
N/A
in our report). Now, even though our records are deficient (see
the two records examples below), they can be treated uniformly, i.e.
without implementing exception handling for missing values:
Example:
person!: make object! [
first-name: "N/A" last-name: "N/A"
phone: "N/A"
email: "N/A" website: "N/A"
address: "N/A"
]
persons: make block! 1000
;- deficient record: no email address or web site
append persons make person! [
first-name: "Tom" last-name: "Smith"
phone: "555-555-5555"
address: "Main Street, Ukiah, CA 95482"
]
;- deficient record: no phone number or web site
append persons make person! [
first-name: "Bill" last-name: "Jones"
address: "Main Street, Ukiah, CA 95482"
email: "[bjones--earthlink--net]"
]
;- generic display function
display-list: func [field] [
foreach person persons [
print [
person/first-name person/last-name get in person field
]
]
]
list-phone-numbers: does [ display-list 'phone ]
list-email-addresses: does [ display-list 'email ]
list-addresses: does [ display-list 'address ]
list-phone-numbers
list-email-addresses
list-addresses
2. Use objects, when you have hierarchies of structures:
animal!: make object! [ species: "N/A" extinct: "N/A"]
dinosaur: make animal! [ species: "Mamal" extinct: "Yes" lived-from:
1965 lived-to: 1979 ] ;- must lookup those numbers!
cat: make animal! [ species: "Mamal" extinct: "No" likes: "Milk, Mice,
Fireplace" ]
3. Use objects to subcatgorize data:
contact!: make object! [
name: "N/A" first-name: "N/A"
phone: "N/A"
address: "N/A"
email: "N/A" website: "N/A"
]
company!: make object! [
office: make contact! []
contact: make contact! []
]
rebol-tech: make company! [ office: make contact! [ ....] contact: make
contact! [ .... ] ]
That will enable you to look up office related data as
rebol-tech/office/... and contact related data as rebol-tech/contact/...
with both subcategories having the same structure, i.e. you can again
implement a single set of functions to process both types of data.
Andrew Martin wrote:
[8/11] from: atruter:labyrinth:au at: 3-Sep-2003 10:29
Hi Gregg,
I think your points plus Elan's object observations could almost be placed
verbatim in the Cookbook! I'm glad that you could verbalise what many on
this list (including me finally) do at an intuitive level. While I have no
difficulty explaining [to non-REBOL folks] *what* my REBOL code does, I
have often found it difficult to explain how and why the final solution is
expreseed the way it is. I think you just made that task a bit easier! ;)
Regards,
Ashley
[9/11] from: greggirwin:mindspring at: 2-Sep-2003 20:28
Hi Ashley,
AT> ...I have often found it difficult to explain how and why the final solution is
AT> expreseed the way it is. I think you just made that task a bit easier! ;)
Thanks for the kinds words. :)
-- Gregg
[10/11] from: rebol:techscribe at: 3-Sep-2003 9:25
Hi.
Another issue that comes to mind is name space and side effects. If a
function introduces a word that will be used within the context of that
function only, then that word should be declared local to the function
in order to avoid name clashes that may result in unexpected side effects:
f: func [ /local word] [
word: "function's local word."
]
This prevents errors that result from setting a global word to some value, then evaluating
a function that happens to set the same word locally to a different value, and then continuing
to use the global word. This is especially critical if functions are reused either by
their original author or by some unsuspecting consumer. Example:
;--- consumer's script
REBOL []
;- globally declared word
erase-harddrive-completely?: NO
;- evaluating function from a downloaded library
do %down-loaded-library ;- contains function list-directory
this-dir-contents: list-directory
.
.
.
if erase-harddrive-completely? [ erase-harddrive-completely ]
;--- end user's script
So, what's the problem? Imagine the function "borrowed" from the downloaded library looks
like this:
;--- function in downloaded library --
list-directory: func [ /interactive ] [
erase-harddrive-completely?: "No"
directory-listing: read %.
if interactive [
display-directory-listing
erase-harddrive-completely?: ask "Should I erase the hard drive completely?"
]
directory-listing
];---- end libraray function ----
Note that the user's script sets erase-harddrive-completely? to the logic! value No,
whereas the function sets the word erase-harddrive-completely? to the string! "No". The
string! "No", however, evaluates as a logic! True:
>> a: No
== false
>> if a [ print "yes" ]
== none
>> a: "No"
== "No"
>> if a [ print "yes" ]
yes
>>
Thus, the harddrive will be erased, even though the library function consumer smartly
set the erase-harddrive-completely? word to No.
If, however, the library function provider had used a /local word in his function, then
it would have not had any affect on the global word.
Take Care,
Elan
[11/11] from: reboler:bol at: 3-Sep-2003 17:31
Hi Folks!,
Thanks to all of you. Elan, Greg, Ashley and Andrew.
These are real good tips that makes me change the way I was programming.
Other things I think should be interesting is the benchmark of functions..
a) conditional loops.. 'repeat instead of 'foreach, 'forall instead of
'foreach.;
b) parse or load/markup (if using parse is faster than 'load/markup.
You know, things like that. ok.
Thank you again.
Best regards,
-DJ
----- Original Message -----
From: "Elan" <[rebol--techscribe--com]>
To: <[rebol-list--rebol--com]>
Sent: Wednesday, September 03, 2003 1:25 PM
Subject: [REBOL] Re: About Standards and Best Practices
> Hi.
> Another issue that comes to mind is name space and side effects. If a
<<quoted lines omitted: 5>>
> ]
> This prevents errors that result from setting a global word to some value,
then evaluating a function that happens to set the same word locally to a
different value, and then continuing to use the global word. This is
especially critical if functions are reused either by their original author
or by some unsuspecting consumer. Example:
> ;--- consumer's script
> REBOL []
<<quoted lines omitted: 9>>
> ;--- end user's script
> So, what's the problem? Imagine the function "borrowed" from the
downloaded library looks like this:
> ;--- function in downloaded library --
> list-directory: func [ /interactive ] [
<<quoted lines omitted: 3>>
> display-directory-listing
> erase-harddrive-completely?: ask "Should I erase the hard drive
completely?"
> ]
> directory-listing
> ];---- end libraray function ----
>
> Note that the user's script sets erase-harddrive-completely? to the logic!
value No, whereas the function sets the word erase-harddrive-completely? to
the string! "No". The string! "No", however, evaluates as a logic! True:
Notes
- Quoted lines have been omitted from some messages.
View the message alone to see the lines that have been omitted