octave-maintainers
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: ismember bugs


From: Søren Hauberg
Subject: Re: ismember bugs
Date: Sat, 11 Aug 2007 11:38:51 +0200
User-agent: Thunderbird 1.5.0.12 (X11/20070604)

Søren Hauberg skrev:
Sorry about replying to my own post, but I used part of my just-posted code to patch the current implementation of ismember. I still don't understand that implementation, but I can fix the bugs (patch attached). This assumes that the cellstr bug is fixed.
Once again I'm replying to my own post. I'm beginning to feel like a spammer... Anyway, attached is a patch that I think cleans ismember up a bit. It also adds support for a second output argument like matlab does. Assuming the cellstr bug gets fixed it should pass all tests. I still don't really understand the code, so I don't think this patch should be applied before somebody else has tested it as well. I did _not_ add support for the 'rows' argument that matlab has. I'll leave that as an exercise to the reader :-)

Søren
--- /home/sh/Programmer/share/octave/2.9.13/m/set/ismember.m    2007-08-10 
22:48:08.000000000 +0200
+++ ismember.m  2007-08-11 11:34:39.000000000 +0200
@@ -18,59 +18,68 @@
 ## 02110-1301, USA.
 
 ## -*- texinfo -*-
-## @deftypefn {Function File} {} ismember (@var{A}, @var{S})
-## Return a matrix the same shape as @var{A} which has 1 if
-## @code{A(i,j)} is in @var{S} or 0 if it isn't.
+## @deftypefn {Function File} address@hidden, @var{index}] = ismember 
(@var{A}, @var{S})
+## Return a matrix @var{bool} the same shape as @var{A} which has 1 if
+## @code{A(i,j)} is in @var{S} or 0 if it isn't. If a second output argument
+## is requested, the indexes into  @var{S} of the matching elements is
+## also returned.
 ## @seealso{unique, union, intersection, setxor, setdiff}
 ## @end deftypefn
 
 ## Author: Paul Kienzle
 ## Adapted-by: jwe
 
-function c = ismember (a, S)
+function [c, index] = ismember (a, S, rows_opt)
 
-  if (nargin != 2)
+  if (nargin != 2 && nargin != 3)
     print_usage ();
   endif
 
+  ## We currently don't handle the 'rows' argument
+  if (nargin == 3)
+    ## XXX: Should this rather be an error?
+    warning("ismember: 'rows' argument not supported");
+  endif
+
+  ## Convert char matrices to cell arrays
+  if (ischar(a))
+    a = cellstr(a);
+  endif
+  if (ischar(S))
+    S = cellstr(S);
+  endif
+  
+  ## Input checking
+  if ( !isa(a, class(S)) )
+    error("ismember: both input arguments must be the same type");
+  endif
+  if (iscell(a) && !iscellstr(a))
+    error("ismember: cell arrays may only contain strings");
+  endif
+  if (!isnumeric(a) && !iscell(a))
+    error("ismember: input arguments must be arrays, cell arrays, or strings");
+  endif
+  
+  ## Do the actual work
   if (isempty (a) || isempty (S))
     c = zeros (size (a), "logical");
   else
-    if (iscell (a) && ! iscell (S))
-      tmp{1} = S;
-      S = tmp;
-    endif
-    if (! iscell (a) && iscell (S))
-      tmp{1} = a;
-      a = tmp;
-    endif
-    S = unique (S(:));
-    lt = length (S);
-    if (lt == 1)
-      if (iscell (a) || iscell (S))
-        c = cellfun ("length", a) == cellfun ("length", S);
-        idx = find (c);
-       if (isempty (idx))
-         c = zeros (size (a), "logical");
-       else
-         c(idx) = all (char (a(idx)) == repmat (char (S), length (idx), 1), 2);
-       endif
-      else
+    if (numel (S) == 1)
+      if (iscell(a))
+        c = strcmp(a, S);
+      else # 'a' and 'S' are matrices
         c = (a == S);
       endif
+      index = double(c);
     elseif (numel (a) == 1)
-      if (iscell (a) || iscell (S))
-        c = cellfun ("length", a) == cellfun ("length", S);
-        idx = find (c);
-       if (isempty (idx))
-         c = zeros (size (a), "logical");
-       else
-          c(idx) = all (repmat (char (a), length (idx), 1) == char (S(idx)), 
2);
-          c = any(c);
-       endif
-      else
-        c = any (a == S);
+      if (iscell (a))
+        f = find(strcmp(a, S), 1);
+      else # 'a' and 'S' are matrices
+        f = find(a == S, 1);
       endif
+      c = !isempty(f);
+      index = f;
+      if (isempty(index)), index=0; endif
     else
       ## Magic:  the following code determines for each a, the index i
       ## such that S(i)<= a < S(i+1).  It does this by sorting the a
@@ -101,16 +110,22 @@
       ## easy to now check membership by comparing S(a_idx) == a.  This
       ## magic works because S starts out sorted, and because sort
       ## preserves the relative order of identical elements.
+      lt = length(S);
+      [S, Sidx] = sort(S);
       [v, p] = sort ([S(2:lt); a(:)]);
       idx(p) = cumsum (p <= lt-1) + 1;
       idx = idx(lt:end);
-      if (iscell (a) || iscell (S))
+      if (iscell (a))
         c = (cellfun ("length", a)
-            == reshape (cellfun ("length", S(idx)), size (a)));
+             == reshape (cellfun ("length", S(idx)), size (a)));
         idx2 = find (c);
         c(idx2) = all (char (a(idx2)) == char (S(idx)(idx2)), 2);
-      else
+        index = zeros(size(c));
+        index(c) = Sidx(idx(c));
+      else # 'a' and 'S' are matrices
         c = (a == reshape (S (idx), size (a)));
+        index = zeros(size(c));
+        index(c) = Sidx(idx(c));
       endif
     endif
   endif
@@ -121,7 +136,7 @@
 %!assert (ismember ('abc', {'abc', 'def'}), true);
 %!assert (isempty (ismember ([], [1, 2])), true);
 %!xtest assert (ismember ('', {'abc', 'def'}), false);
-%!xtest fail ('ismember ([], {1, 2})', 'error:.*');
+%!fail ('ismember ([], {1, 2})', 'error:.*');
 %!fail ('ismember ({[]}, {1, 2})', 'error:.*');
 %!assert (ismember ({'foo', 'bar'}, {'foobar'}), logical ([0, 0]))
 %!assert (ismember ({'foo'}, {'foobar'}), false)

reply via email to

[Prev in Thread] Current Thread [Next in Thread]