how do you properly exit a called method?

Hi everyone,

I am trying to end a script upon clicking “quit”… I am not sure of what the best way to do this is, but right now-- If I click “ok” when the answer is not an integer, and am given the alert “you must enter a number”, and then click quit, it will take me back to where I was previously, because it’s returning back from the previous method call…

So the question is-- is there a way to pop the stack? Or is there an entirely better way to do this sort of thing?

Thank you.

-patrick



my do_something()

on do_something()
	
	set question to (display dialog "enter a number:" default answer "" buttons {"Ok", "Quit"} default button 1)
	set button to button returned of question
	
	if button = "Quit" then return
	
	try
		-- "" can be convertedd to integer
		if x = "" then x = "a"
		set x to text returned of question as integer
	on error
		display dialog "Answer must be a number!" buttons {"Ok"} default button 1
		my do_something()
	end try
	
	display dialog "did I get here?"
end do_something


I generally recommend not calling a handler from within itself and in this instance you don’t even need a handler although you could certainly use one. Here is an example just using a repeat rather than a handler.

set exitLoop to false

repeat until exitLoop is true
	set {button, x} to {button returned, text returned} of (display dialog "enter a number:" default answer "" buttons {"Ok", "Quit"} default button 1 cancel button 2)
	try
		-- "" can be convertedd to integer
		if x = "" then set x to "a"
		x as integer
		set exitLoop to true
	on error
		display dialog "Answer must be a number!" buttons {"Ok"} default button 1
	end try
end repeat

display dialog "did I get here?"

A slight variation of what James said:

repeat
	display dialog "Enter a number:" default answer ""
	set x to text returned of result
	
	try
		if x is "" then error
		set x to x as integer
		exit repeat
	on error
		display dialog "Answer must be a number!" buttons {"OK"} default button 1
	end try
end repeat

Or even a slight change to your version, Patrick:

my do_something()

on do_something()
	
	display dialog "enter a number:" default answer "" buttons {"Ok", "Quit"} default button 1 cancel button 2
	set x to text returned of result
	
	try
		-- "" can be convertedd to integer
		if x = "" then x = "a"
		set x to x as integer
	on error
		display dialog "Answer must be a number!" buttons {"Ok"} default button 1
		my do_something()
	end try
	
	display dialog "did I get here?"
end do_something

display dialog:

Also know as error number -128, which will automatically stop your script if you don’t handle it differently.

Ahh… Cool… Thank you both for the help! Also, thanks for pointing out the if x = “” then error… That’s much more elegant than x=“a”!

-patrick

Ok, so now I am curious-- This is the actual context of what my script is doing. Do you guys still advise that I don’t do this with calling handlers within handlers? What would be an alternate way to structure this with repeats?

Thanks…

-patrick

my start_program()

on start_program()
	set a to random number from 1 to 10
	set b to random number from 10 to 99
	set c to {"times", "plus", "minus"}
	set type to item ((random number (count c) - 1) + 1) of c
	my generate_question(a, b, c, type)
end start_program

on generate_question(a, b, c, type)
	set my_dialog to "Hey, What is " & (a as string) & " " & type & " " & (b as string) & "?"
	
	if type = "plus" then
		set answer to a + b
	else if type = "minus" then
		set answer to a - b
	else if type = "times" then
		set answer to a * b
	end if
	
	set question to (display dialog my_dialog default answer "I Don't Know!" buttons {"That's my answer!", "Quit"} default button 1 cancel button 2)
	
	set button to button returned of question
	set x to text returned of question
	
	try
		if x = "" then error
		set x to x as integer
	on error
		display dialog "Answer must be a number!" buttons {"Ok"} default button 1
		my generate_question(a, b, c, type)
	end try
	
	set positive to {"you rock", "awesome", "correct", "word up", "yeah", "oh baby", "rockin", "good", "yay", "great", "fantastic", "you be so smart"}
	set negative to {"lame", "idiot", "so stupid", "that was easy you are dumb", "geez", "not too bright"}
	
	if x = answer then
		say item ((random number ((count positive) - 1)) + 1) of positive
		my start_program()
	else
		say item ((random number ((count negative)) - 1) + 1) of negative
		my generate_question(a, b, c, type)
	end if
end generate_question

Your way certainly works, but just so you can see something with repeat loops here’s how I would have written it. I think the script is better this way because 1) you’re not resetting the variables positive, negative, and c with every iteration of the script and 2) you’re not calculating the variable counts multiple times either. These things are done once at the beginning of the script.

I’m not sure why you generated the random numbers like ((random number (count c) - 1) + 1) instead of (random number from 1 to cCount) either. Also I usually like to set all of my variables at the beginning of the script that way I can quickly find and change things when needed.

property positive : {"you rock", "awesome", "correct", "word up", "yeah", "oh baby", "rockin", "good", "yay", "great", "fantastic", "you be so smart"}
property negative : {"lame", "idiot", "so stupid", "that was easy you are dumb", "geez", "not too bright"}
property c : {"times", "plus", "minus"}

property dialog1DefaultAnswer : "I Don't Know!"
property dialog1Buttons : {"That's my answer!", "Quit"}
property dialog2Comment : "Answer must be a number!"
property dialog2Buttons : {"Ok"}

set positiveCount to count of positive
set negativeCount to count of negative
set cCount to count of c

repeat
	-- get question
	set a to random number from 1 to 10
	set b to random number from 10 to 99
	set type to item (random number from 1 to cCount) of c
	
	-- calculate answer
	if type = "plus" then
		set answer to a + b
	else if type = "minus" then
		set answer to a - b
	else if type = "times" then
		set answer to a * b
	end if
	
	-- get answer from user
	set my_dialog to "Hey, What is " & (a as text) & " " & type & " " & (b as text) & "?"
	repeat
		display dialog my_dialog default answer dialog1DefaultAnswer buttons dialog1Buttons default button 1 cancel button 2
		set {button, x} to {button returned, text returned} of result
		try
			if x = "" then error
			set x to x as number
			exit repeat
		on error
			display dialog dialog2Comment buttons dialog2Buttons default button 1
		end try
	end repeat
	
	-- compare user answer to calculated answer
	if x = answer then
		say item (random number from 1 to positiveCount) of positive
	else
		say item (random number from 1 to negativeCount) of negative
	end if
end repeat

James:

I’ve only begun to use simple handlers in my own scripts, and have (already) found it useful on occasion to call a handler ‘from within itself’… so far without untoward incident.

Is there a specific reason you think the practice should be avoided?

Thanks.

Peter B.