2013-10-16 28 views
7

我有一個緩存評估的功能。作爲參數之一,它需要一個函數句柄。在某些情況下,函數句柄是無法訪問的,我不太明白爲什麼。下面的例子說明了什麼讓我難住了:什麼時候可以傳遞一個函數句柄?

>> A.a = @plus; feval(@A.a, 1, 1) 

ans = 

    2 

>> clear A 
>> A.a.a = @plus; feval(@A.a.a, 1, 1) 
Error using feval 
Undefined function 'A.a.a' for input arguments of type 'double'. 

所以,如果我有存儲爲結構成員的功能手柄,我可以把它傳遞罰款,如果它是一個層次深,但如果它不是兩層深。在我的真實使用案例中,我有一個結構D,其中包含許多(117)各種類的實例,所以實際上我有stct.obj.meth,其中stct是一個結構,obj是一個類實例/對象,而meth是一種方法。通過@stct.obj.meth失敗,但如果我分配A = stct.obj,則通過@A.meth成功。

在什麼情況下,我可以傳遞一個函數句柄作爲參數,這樣它仍然可以通過堆棧訪問?


編輯:雖然在上面的使用情況下,我可以簡單地刪除@因爲@plus已經是一個功能句柄。然而,考慮這裏的情況:

>> type cltest.m 

classdef cltest < handle 
    methods 
     function C = mymeth(self, a, b) 
      C = a + b; 
     end 
    end 
end 

>> A.a = cltest(); 
>> feval(@A.a.mymeth, 1, 1) 
Error using feval 
Undefined function 'A.a.mymeth' for input arguments of type 'double'. 
>> b = A.a; 
>> feval(@b.mymeth, 1, 1) 

ans = 

    2 

在這種情況下,我需要@ ... A.a.mymeth

+0

我沒有爲什麼'@ Aamymeth'不會產生有效的函數句柄很好的解釋,但值得注意的是,在你的第一個例子中,額外的'@'不需要。你可以使用'A.a = @plus; feval(A.a,1,1);'因爲'A.a'已經是一個函數指針。 – nispio

+0

@nispio是的。這是早先的,現在被刪除的答案的要點。 – gerrit

回答

5

介紹類是一個大問題MATLAB。事實上,這麼大,他們今天仍然不能正常工作。您的示例顯示結構訪問和類方法訪問衝突,因爲它們必須重載點'。'的含義。並沒有得到它無縫工作。當您在MATLAB控制檯上通過名稱明確調用類方法時,它們或多或少地工作正常,例如,在你的例子>> A.a.mymeth(1,1)。但是,當你有任何類型的間接性時,它很快就會崩潰。

您試圖通過>> @A.a.mymeth得到函數句柄,這是MATLAB無法理解的,可能是因爲它被混合結構/類的東西弄糊塗了。試圖解決使用str2func也無法正常工作。它也適用於顯式名稱訪問,如here所示。它爲你的例子打破了,例如>> str2func('b.mymeth')。它甚至不在課堂上工作。嘗試其他indirections,看他們失敗。

此外,MATLAB不喜歡給你一個類方法的句柄。它沒有功能。無法一次獲取所有函數句柄,甚至無法通過名稱字符串進行動態獲取。

我在這裏看到三個選項。首先,儘可能嘗試改變你的程序。這些函數是否需要坐在classdef中?

其次,請遵循您的或nispio的解決方法。它們都創建一個臨時變量來保存對類實例的引用,以創建對其成員方法的非混合訪問。問題是,它們都需要明確命名該函數。你必須明確地把這個代碼用於每個涉及的功能。沒辦法將其抽象出來。

三,從內部給你的班級方法處理作弊。你可以把它們放在一個結構中。

classdef cltest < handle 
    methods 
     function C = mymeth(self, a, b) 
      C = a + b; 
     end 
     function hs = funhandles(self) 
      hs = struct('mymeth', @self.mymeth, ... 
         'mymeth2', @self.mymeth2); 
     end 
    end 
end 

然後,您可以通過名稱甚至是動態訪問句柄。

>> A.a = cltest; 
>> feval(A.a.funhandles.mymeth, 1, 1); 
>> feval(A.a.funhandles.('mymeth'), 1, 1) 

ans = 

    2 

但要小心,通過使用此功能,您可以從外部訪問Access = private方法。

1

試試這個:

feval(@(varargin)A.a.mymeth(varargin{:}),1,1); 

這是一個有點缺憾,但它應該工作。

編輯:

它的工作原理是通過創建一個Anonymous Function採用可變數目的參數,並轉儲這些參數到方法​​的方式。所以你實際上並沒有傳遞一個指向函數A.a.mymeth的指針,而是傳遞一個指向函數的指針,這個函數被稱爲A.a.mymeth

實現同樣的事情,而無需使用varargin將是一種替代方法:

feval(@(x,y)A.a.mymeth(x,y),1,1); 

這將創建一個接受兩個參數的匿名函數,並將它們傳遞到A.a.mymeth

<speculation>我認爲它必須是一元函數句柄運算符@工作方式的固有方式。 Matlab解析器可能會查看@token並決定是否token是一個有效的函數。在a.mymeth的情況下,足夠聰明地確定mymetha的成員,然後返回適當的句柄。但是,當它看到A.a.mymeth時,它可能會發現A不是類,A也沒有名爲a.mymeth的成員,因此沒有找到有效的函數。這似乎是一個事實,即這個工程的支持:

A.a.a = @plus; feval(A.a.a,1,1) 

,這並不:

A.a.a = @plus; feval(@A.a.a,1,1) 

</speculation>

+0

我不認爲這比我自己的'B = A.a; feval(@ b.mymeth,1,1)'。問題是:這裏發生了什麼? – gerrit

+0

您必須小心所示的解決方法,因爲如果在調用'feval'之前最終覆蓋b,那麼您將遇到問題。查看我的帖子編輯,瞭解我的解決方案工作原因。 – nispio

0

您可以通過引入獨立的功能,糾正什麼@運營商沒有做繞過它:

function h=g(f) 
x = functions(f); 
if ~strcmp(x.type, 'anonymous') 
    h = evalin('caller', ['@(varargin)' x.function '(varargin{:})']); 
else 
    h = f; 
end 
end 

現在對於你的例子:

>> feval(g(@A.a.mymeth), 1, 1) 
ans = 
    2 
>> feval(g(@b.mymeth), 1, 1) 
ans = 
    2 

我認爲這將具有最小的影響在你的代碼上。你可以使它更加優雅,但不夠健壯和/或可讀。該uplus方法不爲function_handle類定義的,所以你可以在你的路徑文件夾中創建@function_handle某處uplus.m與此內容:

function h=uplus(f) 
x = functions(f); 
if ~strcmp(x.type, 'anonymous') 
    h = evalin('caller', ['@(varargin)' x.function '(varargin{:})']); 
else 
    h = f; 
end 
end 

現在你只需要使用[email protected]而不是@。對於你的例子:

>> feval([email protected], 1, 1) 
ans = 
    2 
>> feval([email protected], 1, 1) 
ans = 
    2 
相關問題