Checking that input is a positive number

I wrote this to check a user’s input (for the number of copies they want on a print script) to make sure they didn’t enter a letter, or a zero, or a negative number.

It seem like the kind of thing that could have been done with less code. Just wondering if anyone has a more efficient way of doing what I did here:

repeat
	set StartOver to false
	set InputNum to text returned of (display dialog "How many copies?" default answer "")
	try
		set NumCopies to InputNum as integer
	on error
		display dialog "Expected a number?!? Try again" with icon 0 buttons "Error!" giving up after 2
		set StartOver to true
	end try
	if StartOver is false then
		if NumCopies is greater than 0 then
			exit repeat
		else
			display dialog "Positive whole numbers only!" with icon 0 buttons "Error!" giving up after 2
		end if
	end if
end repeat

Browser: Safari 419.3
Operating System: Mac OS X (10.4)

How about this?

set defaultAnswer to ""
repeat
	try
		display dialog "How many copies?" default answer defaultAnswer
		set numCopies to (text returned of result) as integer
		if numCopies is less than 1 then error
		exit repeat
	on error number errNum
		if errNum is -128 then error number -128 -- User canceled dialog
		set defaultAnswer to "Enter a positive whole number!"
	end try
end repeat

Hi Matt,

I would do some automatic corrections at once

repeat
	set InputNum to text returned of (display dialog "How many copies?" default answer "1")
	try
		set NumCopies to InputNum as integer
		if NumCopies < 0 then set NumCopies to -NumCopies
		if NumCopies = 0 then error
		exit repeat
	on error
		display dialog "Expected a number > 0 ?!? Try again" with icon 0 buttons "Error!" giving up after 2
	end try
end repeat

btw: some suggestions for your print pdf script

-- This droplet processes both files or folders of files dropped onto the applet
property YourPrinterPanther : "NameOfYourPrinter on Address" --Name as it appears in the Printer List in Panther
property YourPrinterTiger : "NameOfYourPrinter" --Name as it appears in the Printer List in Tiger
property YourPrinter : ""
property NumCopies : 1

on open these_items
	setup_printer()
	tell application "Finder"
		activate
		set InputNum to the button returned of (display dialog "How many copies?" buttons {"1", "2", "3"} default button "1")
		set NumCopies to InputNum as real
	end tell
	process_folder(these_items)
	tell application "Finder"
		activate
		display dialog ("Finished!") buttons " " giving up after 1
	end tell
end open

-- this sub-routine processes folders
on process_folder(these_items)
	repeat with this_item in these_items
		if (folder of (info for this_item as alias)) then
			tell application "Finder" to set sub_items to (get files of this_item)
			process_folder(sub_items)
		else
			my process_item(this_item)
		end if
	end repeat
end process_folder

-- this sub-routine prints the files
on process_item(this_item)
	tell application "System Events"
		tell (info for this_item as alias) to if its alias or (its kind does not contain "PDF") then return -- skip aliases and no pdf files
	end tell
	set thisPath to quoted form of POSIX path of (this_item as alias)
	--On the next line, change "letter" to whatever paper size you need.
	set theShell to ("lpr -P " & YourPrinter & " -#" & NumCopies & " " & thisPath & " -o media=letter")
	try
		do shell script theShell
	end try
end process_item

on setup_printer()
	tell application "Printer Setup Utility" --This checks which system you are in, thanks to Kai by the way.
		if ((system attribute "sysv") ≥ 4160) then
			set current printer to printer YourPrinterTiger
			set YourPrinter to YourPrinterTiger
		else
			set current printer to printer YourPrinterPanther
			set YourPrinter to YourPrinterPanther
		end if
	end tell
end setup_printer

My submission.

repeat
	try
		tell (text returned of (display dialog "How many copies?" default answer "") as integer) to if it is greater than 0 then
			set numCopies to it
			exit repeat
		else
			error
		end if
	on error
		display dialog "Expected a positive whole number?!? Try again" with icon 0 buttons "Error!" giving up after 2
	end try
end repeat

James beat me to it, but my submission is very similar to his:

repeat
	set Num to text returned of (display dialog "How many copies" default answer "1")
	try
		if Num ≠ "0" and Num does not contain "-" then
			set Num to Num as integer
			exit repeat
		else
			error
		end if
	on error
		display dialog "Not a Positive Integer"
	end try
end repeat
repeat
	try
		set theResult to text returned of (display dialog "Enter a positive integer:" default answer "") as number
		if not (isPositiveInt(theResult)) then error
		exit repeat
	on error
		display dialog "HURRR NOT A POSITIVE INTEGER LOLS."
	end try
end repeat

on isPositiveInt(input)
	if not (class of input is integer) or not (input > 0) then return false
	return true
end isPositiveInt
  1. If you don’t enter something that can be coerced to a number, the error catch triggers. Don’t need to evaluate anything other than that.

  2. The evaluation function is portable, in case you want to test other numbers–or other objects entirely–later.

If the user doesn’t enter an integer > 0, then don’t make any copies. Anybody who can’t enter an integer > 0 doesn’t deserve copies. :slight_smile:

gl,

Wahah. This is the correct answer.

How about this?

on isPositiveInt(input)
	return not (not (input's class is integer) or not (input > 0))
end isPositiveInt

What does this gain you over the more readable version? Imagine three or six months from now: which one is going to be easier to parse, understand, and alter if needed? Since you’re not really gaining anything significant, performance-wise, and you aren’t actually reducing clutter, I can’t say I’d recommend doing it that way over the first.

Remember, just because you can combine lots of things into a single line doesn’t mean you should. :wink:

It’s just an extra boolean operator. :expressionless:

Or, indeed:

on isPositiveInt(input)
	return (input's class is integer) and (input > 0)
end isPositiveInt

I knew if I put that up I would get a number of new perpectives. Thanks to everyone.

For what it’s worth, I am going with Bruce’s first suggestion. I like the “defaultAnswer” approach over an error dialog that has to clear. And I don’t need to do another interger check in the script so a subroutine is not really needed in this case.

Thanks again.

-Matt

Browser: Safari 419.3
Operating System: Mac OS X (10.4)

But it’s not as straightforward to read though aloud. I deal with this kind of stuff in Carbon and Cocoa all the time, and it really does make a difference down the road. The apostrophe, honestly, doesn’t help either. (I’ve ALWAYS hated that about AppleScript. What a terrible idea it was to add it to the language.)

It’s not an issue of being only one more operator, it’s that it doesn’t read through as cleanly. I really don’t see the benefit of combining it into one line, other than to say it can be done.

Right now you don’t, but who knows what you might do later. Portable code is good code.

Oh, I am definitely keeping a copy of that one in my “working scripts” folder. Just not using it this time.

Actually, I just changed my mind…

The subroutine is the only one that throws an error if you enter a decimal value like “3.4”. The others round up or down. I combined the “defaultAnswer” approach with the sub-routine approach:

set defaultAnswer to ""
repeat
	try
		set theResult to text returned of (display dialog "Enter a positive integer:" default answer defaultAnswer) as number
		if not (isPositiveInt(theResult)) then error
		exit repeat
	on error number errNum
		if errNum is -128 then error number -128 -- User canceled dialog
		set defaultAnswer to "Enter a positive whole number!"
	end try
end repeat

on isPositiveInt(input)
	return (input's class is integer) and (input > 0)
end isPositiveInt

Three less operators, even better!

A dialog in a handler, for anyone who’s interested:

on askForPositiveInteger(someDialogText)
	set defaultAnswer to ""
	repeat
		try
			display dialog someDialogText default answer defaultAnswer
			text returned of result as number
			
			if (class of result is integer) and (result > 0) then return result
			error
		on error number errNum
			if errNum is -128 then error number -128 -- User canceled dialog
			set defaultAnswer to "Enter a positive whole number!"
		end try
	end repeat
end askForPositiveInteger

set test to askForPositiveInteger("Enter a positive integer:")

You can change the the greater than comparison to meet your needs. If you want to accept decimals, you can use this if statement instead:

if ((class of result is integer) or (class of result is real)) and (result > 0) then return result