2018年4月2日 星期一

函式傳遞的進化論 (2) - function object

上一篇中講到利用函式指標與callback函式可以讓我們的程式更有彈性。
不過在一些物件導向的程式語言(例如Java)中,我們沒辦法直接把函式傳遞給另一個函式

註:在物件導向的程式語言中,函式(function)被稱做方法(method),之後我會使用方法一詞

在Java中萬物都是物件(唔,其實還有primitive type這東西的存在,不過我們先忽略它),方法沒有辦法單獨宣告與使用,它必須依附在某個類別下。

所以一個通用的解法是用一個interface將方法包起來
  1. //Comparator.java
  2. public interface Comparator{
  3. int compare(int first, int second);
  4. }

此時我們的bubbleSort可以將Comparator物件傳遞進來,並且在需要時呼叫Comparator的compare方法(第10行)
  1. //Sorter.java
  2. public class Sorter{
  3. public static void main(String []args){
  4. ...
  5. }
  6. public void bubbleSort(int[] array, Comparator c){
  7. for(int round=0; round<array.length; round++){
  8. for(int i=0; i<array.length-1; i++){
  9. if( c.compare(array[i],array[i+1]) > 0){
  10. int temp = array[i];
  11. array[i] = array[i+1];
  12. array[i+1] = temp;
  13. }
  14. }
  15. }
  16. }
  17. }

同樣當我們需要不同排序邏輯時,只需要實做Comparator介面就好
  1. //SortByAsc.java
  2. public class SortByAsc implements Comparator{
  3. public int compare(int first, int second){
  4. if(first < second){
  5. return -1;
  6. }
  7. else if(first > second){
  8. return 1;
  9. }
  10. else{
  11. return 0;
  12. }
  13. }
  14. }

使用時只要new一個有實做Comparator介面的物件即可。
  1. //Sorter.java
  2. public class Sorter{
  3. public static void main(String []args){
  4. int[] array = {5,2,8,1};
  5. Sorter obj = new Sorter();
  6. obj.bubbleSort(array, new SortByAsc());
  7. for(int elem : array){
  8. System.out.println(elem);
  9. }
  10. }
  11. public void bubbleSort(int[] array, Comparator c){
  12. ...
  13. }
  14. }

這裡的SortByAsc就是所謂的function object,它僅僅是方法(函式)的物件而已。

2018年4月1日 星期日

函式傳遞的進化論 (1) - function pointer

在C語言中,可以使用function pointer (函式指標, 一個特殊的指標用來指向函式) 將一個函式當作參數傳給另一個函式。

  1. #include <stdio.h>
  2.  
  3. int add (int firstNumber, int secondNumber);
  4. int subtract (int firstNumber, int secondNumber);
  5. int calculator (int firstNumber, int secondNumber, int (*fp) (int, int));
  6. int main ()
  7. {
  8. int firstNumber = 1;
  9. int secondNumber = 2;
  10. int (*fp) (int, int) = add;
  11. printf ("%d\n", calculator (firstNumber, secondNumber, fp));
  12. fp = subtract;
  13. printf ("%d\n", calculator (firstNumber, secondNumber, fp));
  14.  
  15. return 0;
  16. }
  17.  
  18. int add (int firstNumber, int secondNumber)
  19. {
  20. return firstNumber + secondNumber;
  21. }
  22.  
  23. int subtract (int firstNumber, int secondNumber)
  24. {
  25. return firstNumber - secondNumber;
  26. }
  27.  
  28. int calculator (int firstNumber, int secondNumber, int (*fp) (int, int))
  29. {
  30. return (*fp) (firstNumber, secondNumber);
  31. }

程式碼如上。宣告2個函式add & subtract以及1個函式calculator。
其中calculator的參數允許傳入一個函式來讓calculator呼叫該函式。
在第10行時同樣宣告一個函式指標fp,並且指向add函式。
第11行時則將函式指標fp作為引數傳遞給calculator函式。因為此時fp指向add函式,因此claculator函式會呼叫add函式。
在13行時則將函式指標fp改成指向subtract函式,因此在14行的calculator會呼叫subtract函式。

以上是教科書講到函式指標時經常舉的例子。
我還在念書時對這段程式常有疑惑:
  1. printf ("%d\n", add(firstNumber, secondNumber));
  2. printf ("%d\n", subtract(firstNumber, secondNumber));
像這樣直接呼叫add & subtract函式不就好了,為什麼還要多宣告一個函式指標來指向函式?讓函式指標呼叫該函式,這不是多此一舉嗎?