2012-01-03 23 views
5

Có một vài triển khai của một lớp băm hoặc từ điển trong kho lưu trữ tệp MathWorks File Exchange. Tất cả những gì tôi đã xem xét sử dụng dấu ngoặc đơn quá tải để tham chiếu chính, ví dụ:matlab subsref: {} với đối số chuỗi không thành công, tại sao?

d = Dict; 
d('foo') = 'bar'; 
y = d('foo'); 

có vẻ như giao diện hợp lý. Mặc dù vậy, nếu bạn muốn dễ dàng có từ điển chứa từ điển khác, hãy sử dụng dấu ngoặc đơn {} thay vì dấu ngoặc đơn, vì điều này cho phép bạn nhận được giới hạn cú pháp của MATLAB (tùy ý, dường như). nhiều niềng răng được cho phép, tức là

t{1}{2}{3} % is legal MATLAB 
t(1)(2)(3) % is not legal MATLAB 

vì vậy, nếu bạn muốn một cách dễ dàng có thể tự điển tổ trong từ điển,

dict{'key1'}{'key2'}{'key3'} 

như là một thành ngữ phổ biến ở Perl và có thể và thường xuyên rất hữu ích trong các ngôn ngữ khác bao gồm cả Python, sau đó trừ khi bạn muốn sử dụng n-1 biến trung gian để trích xuất một mục từ điển n lớp sâu, điều này có vẻ là một lựa chọn tốt. Và nó có vẻ dễ dàng để viết lại các hoạt động subsrefsubsasgn của lớp học để làm điều tương tự cho {} như trước đây họ đã làm cho () và mọi thứ sẽ hoạt động.

Trừ khi nó không xảy ra khi tôi thử.

Đây là mã của tôi. (Tôi đã giảm nó vào một trường hợp tối thiểu. Không từ điển thực tế được thực hiện ở đây, mỗi đối tượng có một chìa khóa và một giá trị, nhưng điều này là đủ để chứng minh vấn đề.)

classdef TestBraces < handle 

    properties 
     % not a full hash table implementation, obviously 
     key 
     value 
    end 

    methods(Access = public) 


     function val = subsref(obj, ref) 
      % Re-implement dot referencing for methods. 
      if strcmp(ref(1).type, '.') 
       % User trying to access a method    
       % Methods access 
       if ismember(ref(1).subs, methods(obj)) 
        if length(ref) > 1 
         % Call with args 
         val = obj.(ref(1).subs)(ref(2).subs{:}); 
        else 
         % No args 
         val = obj.(ref.subs); 
        end 
        return; 
       end     
       % User trying to access something else. 
       error(['Reference to non-existant property or method ''' ref.subs '''']); 
      end 
      switch ref.type 
       case '()' 
        error('() indexing not supported.'); 
       case '{}' 
        theKey = ref.subs{1}; 
        if isequal(obj.key, theKey) 
         val = obj.value; 
        else 
         error('key %s not found', theKey); 
        end 
       otherwise 
        error('Should never happen') 
      end 
     end  

     function obj = subsasgn(obj, ref, value) 
      %Dict/SUBSASGN Subscript assignment for Dict objects. 
      % 
      % See also: Dict 
      % 

      if ~strcmp(ref.type,'{}') 
       error('() and dot indexing for assignment not supported.'); 
      end 

      % Vectorized calls not supported 
      if length(ref.subs) > 1 
       error('Dict only supports storing key/value pairs one at a time.'); 
      end 
      theKey = ref.subs{1}; 
      obj.key = theKey; 
      obj.value = value; 
     end % subsasgn   
    end  
end 

Sử dụng mã này, tôi có thể gán như mong đợi:

t = TestBraces; 
t{'foo'} = 'bar' 

(Và rõ ràng là công việc phân công từ đầu ra hiển thị mặc định cho t.) vì vậy, subsasgn xuất hiện để làm việc một cách chính xác.

Nhưng tôi không thể lấy giá trị (subsref không hoạt động):

t{'foo'} 
??? Error using ==> subsref 
Too many output arguments. 

Thông báo lỗi làm cho không có ý nghĩa với tôi, và một breakpoint tại dòng thực thi đầu tiên của handler subsref của tôi là không bao giờ hit, do đó, ít nhất bề ngoài này trông giống như một vấn đề MATLAB, không phải là một lỗi trong mã của tôi.

Rõ ràng chuỗi đối số cho () subscript ngoặc được phép, vì điều này hoạt động tốt nếu bạn thay đổi mã để làm việc với () thay vì {}. (Ngoại trừ khi đó bạn không thể tổ chức hoạt động subscript, đó là đối tượng của bài tập.)

Xem xét những gì tôi đang làm sai trong mã của mình, bất kỳ hạn chế nào khiến tôi không thể làm được triển khai các từ điển lồng nhau sẽ được đánh giá cao.

Trả lời

9

Câu trả lời ngắn, thêm phương pháp này để lớp học của bạn:

function n = numel(obj, varargin) 
    n = 1; 
end 

EDIT: Câu trả lời dài.

Mặc dù cách chữ ký chức năng của subsref xuất hiện trong tài liệu, nó thực sự là một hàm varargout - nó có thể tạo ra một số biến đối số đầu ra. Cả hai cú đúp và chấm lập chỉ mục có thể tạo ra nhiều kết quả đầu ra, như thể hiện ở đây:

>> c = {1,2,3,4,5}; 
>> [a,b,c] = c{[1 3 5]} 
a = 
    1 
b = 
    3 
c = 
    5 

Số lượng kết quả mong đợi từ subsref được xác định dựa trên kích thước của mảng lập chỉ mục. Trong trường hợp này, mảng lập chỉ mục là kích thước 3, vì vậy có ba kết quả đầu ra.

Bây giờ, nhìn lại tại địa chỉ:

t{'foo'} 

kích thước của mảng chỉ mục là gì? Ngoài ra 3. MATLAB không quan tâm rằng bạn có ý định giải thích điều này như là một chuỗi thay vì một mảng. Nó chỉ thấy rằng đầu vào là kích thước 3 và subsref của bạn chỉ có thể xuất ra 1 điều tại một thời điểm. Vì vậy, các đối số không khớp. May mắn thay, chúng ta có thể sửa chữa mọi thứ bằng cách thay đổi cách MATLAB xác định có bao nhiêu kết quả đầu ra được mong đợi bởi quá tải numel. Được trích dẫn từ liên kết doc:

Điều quan trọng cần chú ý là numel liên quan đến các chức năng phụ và quá tải subsagn . Trong trường hợp chức năng subref bị quá tải để lập chỉ mục cú đúp và dấu chấm (như được mô tả trong đoạn cuối), numel được sử dụng để tính số lượng kết quả mong đợi (nargout) được trả về từ subsref. Đối với hàm quá tải , hàm numel được sử dụng để tính số lượng đầu vào được mong đợi (nargin) được gán bằng cách sử dụng subsasgn. Giá trị nargin cho chức năng subasgn quá tải là giá trị trả về bởi numel cộng 2 (một cho biến được gán cho, và một cho cấu trúc mảng các bảng con).

Là một nhà thiết kế lớp, bạn phải đảm bảo rằng giá trị của n được trả về bởi hàm numel tích hợp phù hợp với thiết kế lớp cho đối tượng đó. Nếu n khác với nargout cho hàm subref bị quá tải hoặc nargin cho hàm subasgn bị quá tải , thì bạn cần quá tải numel để trả về giá trị n là phù hợp với hàm subsref và subsasgn của lớp. Nếu không, MATLAB sẽ tạo ra lỗi khi gọi các chức năng này.

Và ở đó bạn có nó.

+0

Tuyệt vời, hoạt động. Vâng, tôi muốn biết câu trả lời dài. – jmhl

+0

Và cảm ơn câu trả lời dài. Tôi đã đoán nó là một cái gì đó dọc theo những dòng sau câu trả lời đầu tiên của bạn, và điều này làm rõ nó. Tuyệt vời, cảm ơn một lần nữa. – jmhl

+2

Vì R2015b bạn nên thay thế quá tải hàm 'numArgumentsFromSubscript' - xem câu trả lời cho http://stackoverflow.com/questions/8713730/matlab-subsref-with-string-argument-fails-why và Mathworks page https: // www. mathworks.com/help/matlab/matlab_oop/overloading-numel-subsref-and-subsasgn.html. – nekomatic

Các vấn đề liên quan