1. LON-CAPA Logo
  2. Help
  3. Log In
 

Creating Custom Functions

Back to Index

Custom functions are very useful for reusability of code. They allow someone to edit a few variables at the beginning of a problem to change aspects of a problem, making it easy to edit a problem into a new one.

A custom function looks like this in Perl.

sub myFunction(){
# write any code here
}

“sub” indicates that you are creating a function, and myFunction() is the name of your function. You can name your function anything, in this case we chose “myFunction”. Both () and {} must be included after the name of your function. The tags {} indicate where the instructions of the function start and end, so whenever the function is called (more on that later) everything inside of {} is run. This is similar to (), except variables can be places inside there, separated by a comma.

You can format this any way you wish, as long as these elements exist in this order without anything in between. For example, you may wish to write this as:

sub myFunction(){
# write any code here}

However notice that you cannot place actual comments here, as you will comment out your ending bracket.

but writing a function like this will cause an error.

sub myFunction();{
# write any code here
}

A function that adds two numbers would look like this:

sub addNumbers(){
 @input = @_;
 return @input[0]+ @input[1];
}

When using a functions, ‘@_’ represents the array of all arguments passed into the function. The command “@input = @_;” fills the array @input with each argument of the function when it is called. Calling a function is using a function with arguments, if necessary. For example:

addNumbers(1,3,’b’);

would set

@input[0] to 1,
@input[1] to 3, and
@input[2] to b.

As you can see, if you have a function that adds numbers you can cause issues by using words as arguments. Being clear about the usage of a function in the name of the function will help others who might use your code from avoiding these mistakes.

In a function the ‘return’ statement is used to get a value out of a function. Whenever return is seen in a function, it makes that functions value the value that it is told to return. This allows you to use a function in this way:

#my variables
$number = 1;
$anotherNumber = 2;
$answer;
#my function
sub addNumbers(){
 @input = @_;
 return @input[0]+ @input[1];
}
#using my function
$answer = addNumbers(number,anotherNumber);

After this code executes, $answer will be 3.

You can also directly set $answer instead of using return:

#my variables
$number = 1;
$anotherNumber = 2;
#my function
sub setAnswer(){
 @input = @_;
 $answer = @input[0]+ @input[1];
}
#using my function
setAnswer(number,anotherNumber);

In this situation, you might want to declare $answer in the #my variables section. This is because if you try to use $answer before you declare it using setAnswer(), $answer will not exist! This will cause errors.

Directly setting a variable like we just did ($answer = $number1+$number2;) means that the function cannot be used for more than one situation. This makes using return instead of directly setting a variable in a function best in most situations.

#my variables
$answer1;
$answer2;
$answer3;
#my function
sub addNumbers(){
 @input = @_;
 return @input[0]+ @input[1];
}
#using my function
$answer1 = addNumbers(1,4); #sets $answer1 to 5
$answer2 = addNumbers(1,$answer1); #sets $answer2 to 6
$answer3 = addNumbers($answer1, $answer2); #sets $answer3 to 11
#You can even use a variables old value to give it a new one!
$answer3 = addNumbers($answer3, -10); #sets $answer3 to 1

For problem creation, the main advantage of functions is that anyone can edit the arguments (variables that go inside the “()”) of your function to change the problem! A functional example of this would be:

<problem>
<script type="loncapa/perl">
 $ansNumeric = 1;
 $ansDisplay ="";
 @factors = ();
 $numberFactors = &random(2,3,1);
 $chanceNoAns = 20;
 getAns($numberFactors,$chanceNoAns);
 $ans = 1;

 sub getAns(){
  my ($num,$chance) = @_;
  if (&random(1,100,1) >= $chance){setFactors($num);}
    else {noAns();}
  }
 sub setFactors{
  my ($num) = @_;
  for($i=0;$i<$num;$i++){
   @factors[$num] = getPrime();
   $ansNumeric = $ansNumeric * @factors[$num];
   $ansDisplay .= @factors[$num] . ",";
   }
  chop($ansDisplay);
  }
 sub noAns(){
  $ansDisplay = "DNE";
  $numberFactors =2;
  $ansNumeric = -1*abs(getPrime()*getPrime()) ;
  }
 sub getPrime(){
  @primes = (2,2,2,2,3,3,3,7,7,11,11,13);
  return @primes[&random(0,11,1)]*&random(-1,1,2);
  }
</script>

<startouttext />
If <m eval="on">$ x^$numberFactors=$ansNumeric $</m>, then x =
<endouttext />

<customresponse answerdisplay=$ansDisplay>
<answer type="loncapa/perl">
 @input = split(',', $submission);
 if ($ansDisplay eq "DNE")
   {
   if (uc($submission) eq "DNE"){return 'EXACT_ANS';}
   else {return 'INCORRECT';}
   }
 for($n=0;$n<$numberFactors;$n++){
   $ans = $ans*@input[$n];
   if (abs(@input[$n])==1){return 'INCORRECT';}
   if (index(@input[$n], ".") != -1){return 'INCORRECT';}
  }
 if ($ans == $ansNumeric){return 'EXACT_ANS';}
 return 'INCORRECT';
</answer>
<textline readonly="no" spellcheck="none" />
</customresponse>
</problem>

Here is the same problem as a user would see it.

In the previous code, you were able to control the chances that an answer wouldn’t exist and the number of factors by simply changing two variables. This allows anyone who wants to change the problem to easily understand what your code is doing and allows them to change it. This is useful especially if you include comments explaining how it can be changed.

$numberFactors = &random(2,3,1); # keep this an integer > 1
$chanceNoAns = 20; # chance in percent
getAns($numberFactors,$chanceNoAns);

Back to Index