From octave-sources-request at bevo dot che dot wisc dot edu Sun Mar 11 15:59:10 2001 Subject: Re: Cell array support From: Paul Kienzle To: Jianming Cc: octave-sources , "John W. Eaton" Date: Sun, 11 Mar 2001 14:44:11 +0000 On Sun, Mar 11, 2001 at 12:28:52AM +0800, Jianming wrote: > Hi, > Thanks for the comments. First, I would like to admit that I did not think > about compatiability at all when I wrote this patch. I wrote it because I > need it to use it in some octave code of my own. I need to access an array > of matrices, where each matrix can be of different sizes. Using lists with linear indexing doesn't work for you? A = nth(mlist, (c-1)*nr + r); Admittedly, though, the following is cleaner: A = mlist(r,c) > > 1) For consistency, I think Array-Cell.cc is better placed in liboctave/, > following the example of Array-b, Array-ch, Array-d etc. In addition, > Cell.cc and Cell.h should be moved to liboctave as well, again for > consistency, to be similar to dMatrix etc. For modularity I think octave_values should be kept out of liboctave. I think it is great that a new array class private to a specific octave type can be defined with very little work completely outside liboctave (modulo the fill value problem). For example, Ben Sapp may want to extend his support of variable precision arithmetic to arrays, but we surely don't want to dump all of the variable precision arithmetic library into liboctave. > > 2) Apparently, magically, when I dereference the cells, I get empty > matrices, i.e. [](0x0). Perhaps there is some conversion going on? Maybe > John can shed some light on this. > > 3a) Yes, narrowing conversion is used. > > b) IMHO, there should be a clear distinction between 0, [] and {}. 0 is the > base type for scalar, [] for matrix, and {} for cells. Hence, just as > Matrix(2,:) = 0 > should *not* delete the 2nd row of matrix Matrix, > Cell{2,:} = [] > should *not* delete the 2nd row elements of cell Cell. Similarly, just as > Matrix(2,:) = [] > should delete the 2nd row of Matrix, > Cell{2,:}={} > should delete the 2nd row of Cell. > > This is what is implemented in my patch (hopefully). The relationship > between cells and matrix is the same as the relationship between matrix and > scalars. > Hence, for your example, x(m,n)=x(i,j) where x(i,j) is [], will not > delete x(m,n). However, I can still delete using x(i,:)={}. IMHO, "x(idx) = []" for deletion is already pretty arbitrary (shouldn't it produce a dimension conflict?), so I don't put too weight on consistency. I would be happier if deletion was identified in the parser, rather than relying on the semantics of assignment from a particular value. Something like "clear x(idx)" would be even less confusing. And less error prone: consider "x(j) = b" where you are expecting a scalar b, but someone passed b as [] instead. However, interpreting assignment from and empty matrix as deletion of the indexed values is the defacto standard, at least in Matlab, RLaB, SciLab and Octave. > > NOTE: The following two lines should be added to OPERATORS/op-cell.cc in > order that the above works: > DEFASSIGNOP_FN (assign3, cell, cell, assign) > : > : > INSTALL_ASSIGNOP (op_asn_eq, octave_cell, octave_cell, assign3); > > c) I think we should remove the "cellness" of a single element, just as we > remove the "matrixness" of a 1x1 matrix. However, converting back to cell > should not be a problem, because the cell_value of octave_matrix and > octave_scalar will return a cell containing a single element. Hence, we try > to "reduce" an object whenever possible, but also provide the avenue to > "upgrade" an object when needed. This is also done between matrix and > scalar. With automatic type narrowing, you will not be able to consistently handle a cell array containing cell arrays. This may not be a bad thing, since structures provide a more meaningful interface in most cases, and lists can handle the remainder. Regarding implementation, it would be better to use the ASSIGNANYOP rather than ASSIGNOP. See OPERATORS/op-list.cc. That way you don't have to implement cell_value on each and every type. Alternatively, you could put the cell_value logic entirely in ov-base, and have every type inherit it automatically. After all, you just need to return Cell(1,1,*this). > > d) I agree that there should be a distinction between x{i,j} and x(i,j). > However, I'm not proficient enough in lex and yacc to do it. Hence, for my > patch, x{i,j} and x{i,j} are the same. > Ideally, we should have > d=[1 2 3 4 5] > d{1} > ans = [1 2 3 4 5] > Instead, now we have > d{1} > ans = 1 > which is wrong. Curious. By this reasoning, Y(i) should be meaningless if Y is a cell array. I was considering {} as a dereferencing operator which could produce a multi-valued output, or when used as an lvalue, receive a multi-valued output. For example, d=[1 2 3 4 5]; [a,b,c] = d{1:2:5} a = 1 b = 3 c = 5 Or as an lvalue clear d; [d{2:3}] = sort([11 15 13]) d = { {1,1} = [] {1,2} = [ 11 13 15 ] {1,3} = [ 1 3 2 ] } But with your interpretation, this would be d=[1 2 3 4 5]; [a,b,c] = d{1:2:5} error: index out of range [d{2:3}] = sort([11 15 13]) d = { {1,1} = [ 1 2 3 4 5 ] {1,2} = [ 11 13 15 ] {1,3} = [ 1 3 2 ] } I have to say that your interpretation has merit. For one thing, you don't have this weird condition of clearing d before the assignment. It is even less compatible with what matlab does though. Also, you have to decide if you want to be able to conveniently splice cell arrays into an argument list à la Matlab: t = f(x,d{:}) Regarding implementation, there is no need to change lex.l and the change to parse.y is simple. Search for "'{' arg_list" and change the associated function call to reflect that it is using brace indexing rather than parenthesis. You could for example add another parameter to make_index_expression to distinguish between '(' and '{', and extend tree_index_expression in pt-idx.{cc,h} to do what you want. That will be tricky. > One way to do is the following. When {} is used for indexing, the > object, e.g. d, is converted to a cell first before the actual index ops. > For this, we can use the cell_value function. > Once we are able to distinguish between d{1} and d(1), I do not see a > need to preserve the cellness of a single element. The reason you want to preserve the cellness of a single element is when you are calling functions which you want to operate on cells. E.g., function A = applyfn(B, fn) [nr, nc] = size(B); A = cell(size(B)); for i=1:nr for j=1:nc A(i,j) = feval(fn, B(i,j)) end end Now call this on a trivial example: B = { rand(5) }; A = applyfn(B, 'norm') What do you expect? > > Lastly, I'd like to point out that the patch is not meant to be a complete Great! That way issues can be raised before you spend to much time implementing. > one. In particular, complex, complex_matrix, strings etc support are not The other types will be handled automatically if you use ASSIGNANY or move cell_value to ov-base as I mention above. > added. Also, point 3(d) is not implemented. A lot of improvements still > needs to be done. Yes, 3(d) was more than I wanted to take on. But we will get there one step at a time :) > > > Regards > Jianming - Paul ------------------------------------------------------------- Octave is freely available under the terms of the GNU GPL. Octave's home on the web: http://www.octave.org How to fund new projects: http://www.octave.org/funding.html Subscription information: http://www.octave.org/archive.html -------------------------------------------------------------