sorting with revolution

Anything beyond the basics in using the LiveCode language. Share your handlers, functions and magic here.

Moderators: FourthWorld, heatherlaine, Klaus, kevinmiller, robinmiller

Post Reply
marielle
Livecode Opensource Backer
Livecode Opensource Backer

sorting with revolution

Post by marielle » Fri Jan 05, 2007 4:06 pm

Oliver kindly provided some code for sorting files in revolution in the last newsletter

It would be good to see that on the revDeveloper wiki or a place where comments can be made :-D.

The tutorial is obviously for beginner users and all efforts have been made to avoid any level of complexity. With these requirements, that's all good. The division into subroutines is well thought. The functions well commented.

However, a problem something to be careful about when learning to program is to learn good habits, even if this makes the learning a tiny bit more difficult. Habits are easier to learn than unlearn ;-).

I put a few comments... I welcome users of different levels of expertise to tell me what they think of them.

1) In terms of comments, the only reason to use local on top of each function is to make clear the variables that are being used within that function. Personally, I prefer to follow well accepted approaches to code commenting (javadoc and jsdoc). The advantage of this is that I can do automatic code parsing and automatically extract documentation for my code (jsdoc example). Another advantage is that you can more easily discuss your code with somebody from another programming background. Simply by reading the comments, they can understand what your program is about.

So the first function, for instance would become:

Code: Select all

 /**
* Filters the strings "." and ".." from a list
* @param {list} pList. A list with one filename per line.
* @returns A filtered list
*/
function filterDots pList
  [...]
end filterDots
2) Rather than use a local declaration, what I do is make sure I add some data validation code at the start of the function. This helps prevents problems. This also help to clarify the expected value of the variable.

Code: Select all

function filterDots pList
  --- validation ----------------
  if pList is empty then return empty
  put pList into tList
  --- processing ----------------
  filter tList without "."
  filter tList without ".."
  ---
  return tList
end filterDots
This is particularly needed in this function:

Code: Select all

function listFiles pFolder, pRecurse
  --- validation ----------------
  if pFolder is empty then return empty --- you may want to let the user know that the folder is empty. 
  if there is not a folder pFolder then return empty -- you may want to warn the user that the folder doesn't exist
  --- processing ----------------
  set the directory to pFolder
  [...]
end listFiles
3) Variable naming. Careful variable naming is the difference between spending 3 hours or 5 minutes trying to understand some code that you have written in the past.

Code: Select all

function listFiles pFolder, pRecurse
if I am correct, pRecurse is a boolean variable. Something which is expected to only take as value either true or false. It helps to add some prefix that clarifies this. pDoRecurse, pIsRecursing. Simlarly, folder is ambiguous. This could refer to the current one or to the full path. Here, this is about the full path... so let's write it pFolderPath. It makes understanding the logic of the code easier.

4) "if not then..." I see that construct in a lot of code within the community. Another version of this is a "if xxxx then" at the start of a function script with an "end if" at the very end of it. Even with indentation, this obscures the code. Then this forces you to keep in mind the limits under which the code is being applied all way through the function. Why not simply deal with the conditions under which the remaining of the function should or should not be applied and then simply completely forget about them. A minor comment is that a negation is always more difficult to process (cognitively speaking) than an affirmation.

Code: Select all

if tCurrentFiles is not empty then put tCurrentFiles & return after tTotalFiles
If personally prefer to rewrite this like this

Code: Select all

--- condition of exit ------
if tCurrentFiles is empty then return empty
-----------------------
put tCurrentFiles & return after tTotalFiles
Anybody with such tricks for writing better code?
Last edited by marielle on Fri Jan 05, 2007 9:13 pm, edited 2 times in total.

FourthWorld
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 10054
Joined: Sat Apr 08, 2006 7:05 am
Contact:

Post by FourthWorld » Fri Jan 05, 2007 5:58 pm

> Anybody with such tricks for writing better code?

Scripting Style Guide


I like your suggestion about clarifying Boolean params with an "is" prefix. I tend to use a "Flag" suffix myself, but either way it does help clarify the argument's type. I'll add that to the next update to the Style Guide.

marielle
Livecode Opensource Backer
Livecode Opensource Backer

Post by marielle » Fri Jan 05, 2007 9:50 pm

Thanks for the link Richard. A much recommended reading, indeed.

On argument type, in fact, what I do in reality is not have:

Code: Select all

if pFolder is empty then ...
if there is no folder pFolder then ...
but rather have

Code: Select all

 if datatype.validate("path", pFolder) is false then ....
For other variables like boolean or integers, I would sometimes choose to force them to get a value and have:

Code: Select all

put datatype.force("integer", pNumber, -1)into tInteger
or

Code: Select all

put datatype.force("integer", pNumber, 1) into tNumber
This way, it is even less ambiguous what the value is expected to be and all these data validation routines need to be written only once and be pasted, for instance, at stack level.

Clearly, this is adding an extra cost in terms of speed of processing, with a call to a function. However, I find that the benefits in terms of legibility of the code, ease of maintenance and ease of debugging largely outweighs any possible disadvantage.

These functions for data validation look like this:

Code: Select all

/* ____________________________________________________________
|
|   Datatypes and Their validation
|
|________________________________________________________
|
|   @Author:        Marielle  Lange
|   @Company:       Widged.com
|   @Date:       25 Apr 2006
|   @Version:       0.2
|   @Changes since last version:  n/a 
|   @License:        Creative Commons Attribution 2.5 License  http://creativecommons.org/licenses/by/2.5/
|________________________________________________________
|
|   @Dependency:    -
|________________________________________________________ */




function datatype.validate pDataType, pValue
  switch pDataType
  case "number"
    return  number.validate(pValue)
    break
  case "positiveinteger"
    return  positiveinteger.validate(pValue)
    break
  case "boolean"
    return  boolean.validate(pValue)
  case "loc"
    return  loc.validate(pValue)
    break
  case "reformat"
    return  reformat.validate(pValue)
    break
  case "objectRef"
    return  objectRef.validate(pValue)
    break
  end switch
end datatype.validate


/* _______________________________________________________
|
|   number
*/
function number.validate pNumber
  if isnumber(pNumber) then return true
  return false
end number.validate
---
function number.force pNumber, pDefault
  if not isnumber(pNumber) then return pDefault
  return pParam 
end number.force

/* _______________________________________________________
|
|   positive integer
*/
function positiveinteger.validate pData
  if pData is a number and pData > -1 then 
    return true
   end if 
  return false
end positiveinteger.validate
------
function positiveinteger.force pData, pDefault
  if pDefault is empty then put 1 into pDefault
  if pData is not an integer then return pDefault
  if pData  < 1 then return pDefault
  return pData
end positiveinteger.force

/* ____________________________________________________________
|
|  boolean
|
*/
function boolean.validate pData
  if pData is true then 
    return true
  end if
  return false
end boolean.validate

/* ____________________________________________________________
|
|  path
|
*/
function path.validate pData
  if there is a file pData then 
    return true
  end if
  return false
end path.validate 

/* ____________________________________________________________
|
|   objectref.validate
|
*/
function objectref.validate pData
  if exists(pData) then 
    return true
  else
    return false
  end if   
end objectref.validate

/* _______________________________________________________
|
|   RefFormat
|
|   @desc:   The format of a reference to an uiObject       
|
*/
function reformat.validate pRefFormat
  if pRefFormat is among the items of "id,short,long,name" then return true
  return false
end reformat.validate

/* _______________________________________________________
|
|   loc              
|
|    @desc: a x,y location
*/
function loc.validate pLoc
  if item 1 of pLoc is an integer and item 2 of pLoc is an integer then return true
  return false
end loc.validate

/*_________________________________________
|
|    transcriptReservedKeyword
|
*/
function datatype.transcriptReservedKeyword pString
  	if pString is among the lines of the commandnames then return true
  	if pString is among the lines of the constantnames then return true
  	if pString is among the lines of the functionnames then return true
  	if pString is among the lines of the propertynames then return true
  	return false
end datatype.transcriptReservedKeyword


/* ____________________________________________________________
|
|   delimiter.validate
|
*/
function delimiter.validate pData
  put space & ",;|" & cr into tDelims
  if offset(pData, tDelims) > 0 then 
    return true
  end if
  return false
end delimiter.validate
---
function delimiter.force pData
  put space & ",;|" & cr into tDelims
  if offset(pData, tDelims) > 0 then 
    return pData
  else 
    return ","
  end if
end delimiter.force

/* ____________________________________________________________
|
|  file
|
*/
function file.validate pData
  if there is a file pData then 
    return true
  end if
  return false
end file.validate




Well, that's a first draft... comments are welcome.

[second draft now]

marielle
Livecode Opensource Backer
Livecode Opensource Backer

Post by marielle » Wed Jan 17, 2007 3:12 pm

(did clean up the script of the previous post, v0.2 now)

Bernard
Posts: 351
Joined: Sat Apr 08, 2006 10:14 pm

Post by Bernard » Thu Jan 18, 2007 11:05 am

I think these are very sound ideas.

I'm interested in the idea of creating the documentation from the comments, although I rather dislike the presentation of it e.g. in JavaDoc. I think if I go down this route I will have to find a more palatable way of presenting it to myself.

I like the idea of centralized validation routines. I think the dot notation might seem a bit Javascript-ish for some xTalkers though :wink:

marielle
Livecode Opensource Backer
Livecode Opensource Backer

Post by marielle » Thu Jan 18, 2007 1:42 pm

Bernard wrote:I'm interested in the idea of creating the documentation from the comments, although I rather dislike the presentation of it e.g. in JavaDoc. I think if I go down this route I will have to find a more palatable way of presenting it to myself.
The (midly) difficult part is the parsing. Once parsed, you can decide how to present the information. I agree, it would be worth to rethink the browser display option of javadoc
Bernard wrote:I like the idea of centralized validation routines.
The big advantage is really reliability. As I use centralized routines, any time I make an improvement, this improvement carries over for any further work.
Bernard wrote:I think the dot notation might seem a bit Javascript-ish for some xTalkers though :wink:
:wink: .

I find it a lore more legible. I put a dot in any function/handler that is part of my system of reusable components. The part before the dot is the object type the function applies to (this can be something abstract and script specific as cat and dog). The part after corresponds to the usual verb object notation.

In contrast, I put an underscore "_" in any of my custom properties used within these reusable components. This way, I don't need prefixing. I personally find the "u" prefixing to greatly impair legibility and as a psycholinguist specialized in word recognition, I know well enough how this can snowball onto my cognitive processes and force me to use more attention (which is of a limited capacity), and therefore be more prone to errors :shock:.

Post Reply