[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Using "sort!" in a C extension (1.9 problem

Andre Nathan

12/29/2007 5:50:00 PM

Hello

In ruby 1.8, I could use Array#sort! in a C extension like this:

static VALUE
call_sort_bang(VALUE ary)
{
return rb_funcall(ary, rb_intern("sort!"), 0);
}

static VALUE
sort_i(VALUE ary)
{
long v1 = FIX2LONG(RARRAY_PTR(ary)[0]);
long v2 = FIX2LONG(RARRAY_PTR(ary)[1]);
return LONG2FIX(v1 - v2);
}

static VALUE
foo(void)
{
VALUE ary = rb_ary_new();
rb_ary_push(ary, INT2FIX(1));
rb_ary_push(ary, INT2FIX(0));
rb_ary_push(ary, INT2FIX(2));
rb_iterate(call_sort_bang, ary, sort_i, 0);
rb_funcall(rb_mKernel, rb_intern("p"), 1, ary);
return a;
}

This would print "[0, 1, 2]" as expected. However, it doesn't work in
ruby1.9 compiled from svn (checked out today). The problem seems to be
that only one of the array elements is being passed to sort_i(), instead
of a pair of elements as it's done in 1.8.

What is the correct way to do this in 1.9?


Thanks in advance,
Andre


17 Answers

Andre Nathan

12/29/2007 10:12:00 PM

0

My example was a bit contrieved because I could just have used #sort
without a block. What I'm actually doing is sorting an array by the
length of its elements, which are also arrays. That's why I need the
block version of #sort.

On Sun, 2007-12-30 at 02:49 +0900, Andre Nathan wrote:
> Hello
>
> In ruby 1.8, I could use Array#sort! in a C extension like this:
>
> static VALUE
> call_sort_bang(VALUE ary)
> {
> return rb_funcall(ary, rb_intern("sort!"), 0);
> }
>
> static VALUE
> sort_i(VALUE ary)
> {
> long v1 = FIX2LONG(RARRAY_PTR(ary)[0]);
> long v2 = FIX2LONG(RARRAY_PTR(ary)[1]);
> return LONG2FIX(v1 - v2);
> }
>
> static VALUE
> foo(void)
> {
> VALUE ary = rb_ary_new();
> rb_ary_push(ary, INT2FIX(1));
> rb_ary_push(ary, INT2FIX(0));
> rb_ary_push(ary, INT2FIX(2));
> rb_iterate(call_sort_bang, ary, sort_i, 0);
> rb_funcall(rb_mKernel, rb_intern("p"), 1, ary);
> return a;
> }
>
> This would print "[0, 1, 2]" as expected. However, it doesn't work in
> ruby1.9 compiled from svn (checked out today). The problem seems to be
> that only one of the array elements is being passed to sort_i(), instead
> of a pair of elements as it's done in 1.8.
>
> What is the correct way to do this in 1.9?
>
>
> Thanks in advance,
> Andre
>
>


Siep Korteling

12/29/2007 10:34:00 PM

0

Andre Nathan wrote:
> My example was a bit contrieved because I could just have used #sort
> without a block. What I'm actually doing is sorting an array by the
> length of its elements, which are also arrays. That's why I need the
> block version of #sort.

Like this?
a=[[2],[1,2,3],[1],[1,2]]
p a.sort_by{|ar|ar.length}

--
Posted via http://www.ruby-....

Andre Nathan

12/29/2007 11:06:00 PM

0

On Sun, 2007-12-30 at 07:34 +0900, Siep Korteling wrote:
> Like this?
> a=[[2],[1,2,3],[1],[1,2]]
> p a.sort_by{|ar|ar.length}

Yes but in a C extension. The code I have is not working with 1.9.

Andre


Andre Nathan

12/30/2007 12:31:00 AM

0

On Sun, 2007-12-30 at 08:06 +0900, Andre Nathan wrote:
> On Sun, 2007-12-30 at 07:34 +0900, Siep Korteling wrote:
> > Like this?
> > a=[[2],[1,2,3],[1],[1,2]]
> > p a.sort_by{|ar|ar.length}
>
> Yes but in a C extension. The code I have is not working with 1.9.

Well not exactly. sort_by works but I'd prefer to use sort! with a
block, as it does the sorting in-place.

In 1.9, if I inspect "ary" below, I only see the current array element.
In 1.8, it is an array of the two elements which are being compared.

static VALUE
sort_i(VALUE ary)
{
long v1 = FIX2LONG(RARRAY_PTR(ary)[0]);
long v2 = FIX2LONG(RARRAY_PTR(ary)[1]);
return LONG2FIX(v1 - v2);
}

Andre


KUBO Takehiro

12/30/2007 2:00:00 AM

0

Hi,

On Dec 30, 2007 2:49 AM, Andre Nathan <andre@digirati.com.br> wrote:

> rb_iterate(call_sort_bang, ary, sort_i, 0);

Use rb_ary_sort_bang instead of call_sort_bang.
I don't know why call_sort_bang doesn't work.

Andre Nathan

12/30/2007 2:36:00 AM

0

On Sun, 2007-12-30 at 10:59 +0900, KUBO Takehiro wrote:
> Use rb_ary_sort_bang instead of call_sort_bang.
> I don't know why call_sort_bang doesn't work.

Thanks! For some reason I thought rb_ary_sort_bang was static while it
isn't...

It would still be interesting to know why this didn't work, since some
methods are indeed static and rb_funcall() is the only way to call them.

Andre


Andre Nathan

12/30/2007 3:11:00 AM

0

On Sun, 2007-12-30 at 11:36 +0900, Andre Nathan wrote:
> On Sun, 2007-12-30 at 10:59 +0900, KUBO Takehiro wrote:
> > Use rb_ary_sort_bang instead of call_sort_bang.
> > I don't know why call_sort_bang doesn't work.
>
> Thanks! For some reason I thought rb_ary_sort_bang was static while it
> isn't...

Actually...

I was using rb_funcall() because this way "sort!" understands it's being
given a block, while when calling rb_ary_sort_block() directly it
doesnt:

static VALUE
sort_by_length(VALUE ary)
{
long v0 = RARRAY_LEN(RARRAY_PTR(ary)[0]);
long v1 = RARRAY_LEN(RARRAY_PTR(ary)[1]);
return LONG2FIX(v0 - v1);
}

static VALUE
foo(void)
{
VALUE a = rb_ary_new();

rb_ary_push(a, rb_ary_new3(2, INT2FIX(3), INT2FIX(3)));
rb_ary_push(a, rb_ary_new3(1, INT2FIX(2)));
rb_ary_push(a, rb_ary_new3(3, INT2FIX(0), INT2FIX(0), INT2FIX(0)));

rb_iterate(rb_ary_sort_bang, a, sort_by_length, 0);

rb_funcall(rb_mKernel, rb_intern("p"), 1, a);

return a;
}

This prints "[[0, 0, 0], [2], [3, 3]]" and not "[[2], [3,3], [0, 0, 0]]"
as expected. sort_by_length() is never called, and the sub-arrays were
sorted by their contents, because rb_block_given_p() returns false in
rb_ary_sort_bang().

I also tried

rb_block_call(a, rb_intern("sort!"), 0, 0, sort_by_length, 0);

but that has the same problem I had with rb_funcall -- sort_by_length is
given the current array element only as its argument.

Andre


KUBO Takehiro

12/30/2007 4:15:00 AM

0

On Dec 30, 2007 12:11 PM, Andre Nathan <andre@digirati.com.br> wrote:
> On Sun, 2007-12-30 at 11:36 +0900, Andre Nathan wrote:
> > On Sun, 2007-12-30 at 10:59 +0900, KUBO Takehiro wrote:
> > > Use rb_ary_sort_bang instead of call_sort_bang.
> > > I don't know why call_sort_bang doesn't work.

Sorry. This is my mistake.

> but that has the same problem I had with rb_funcall -- sort_by_length is
> given the current array element only as its argument.

How about the following patch to ruby svn trunk?

--- vm_insnhelper.c (revision 14790)
+++ vm_insnhelper.c (working copy)
@@ -652,9 +652,12 @@
else if (argc == 0) {
arg = Qnil;
}
- else {
+ else if (argc == 1) {
arg = argv[0];
}
+ else {
+ arg = rb_ary_new4(argc, argv);
+ }

vm_push_frame(th, 0, FRAME_MAGIC_IFUNC,
self, (VALUE)block->dfp,

I'm not certain this is a correct patch. But at least, sort_by_length
works fine.

Andre Nathan

12/30/2007 3:14:00 PM

0

On Sun, 2007-12-30 at 13:15 +0900, KUBO Takehiro wrote:
> How about the following patch to ruby svn trunk?

Thanks, that fixed it for me! I have another machine with a checkout
from 2007-12-07 which doesn't have this problem, and the code in
vm_yield_with_cfunc() is

if (lambda) {
arg = rb_ary_new4(argc, argv);
}
else {
if (argc == 1) {
arg = *argv;
}
else if (argc > 1) {
arg = rb_ary_new4(argc, argv);
}
else {
arg = rb_ary_new();
}
}

so I believe your patch is correct.

Thanks a lot,
Andre


Ken Bloom

12/31/2007 3:37:00 AM

0

On Sat, 29 Dec 2007 19:30:31 -0500, Andre Nathan wrote:

> On Sun, 2007-12-30 at 08:06 +0900, Andre Nathan wrote:
>> On Sun, 2007-12-30 at 07:34 +0900, Siep Korteling wrote:
>> > Like this?
>> > a=[[2],[1,2,3],[1],[1,2]]
>> > p a.sort_by{|ar|ar.length}
>>
>> Yes but in a C extension. The code I have is not working with 1.9.
>
> Well not exactly. sort_by works but I'd prefer to use sort! with a
> block, as it does the sorting in-place.
>
> In 1.9, if I inspect "ary" below, I only see the current array element.
> In 1.8, it is an array of the two elements which are being compared.
>
> static VALUE
> sort_i(VALUE ary)
> {
> long v1 = FIX2LONG(RARRAY_PTR(ary)[0]); long v2 =
> FIX2LONG(RARRAY_PTR(ary)[1]); return LONG2FIX(v1 - v2);
> }
>
> Andre

This appears to be the result of a ruby 1.9 semantic change for block
arguments.


(from eigenclass.org's summary)
> News semantics for block arguments
>
> |v| now works like the former |v,|:
>
> [RUBY_VERSION, RUBY_RELEASE_DATE] # => ["1.8.5", "2006-08-25"]
> def m; yield 1, 2; end
> m{|v| v} # => [1, 2] # !>
> multiple values for a block parameter (2 for 1)
>
> vs.
>
> [RUBY_VERSION, RUBY_RELEASE_DATE] # => ["1.9.0", "2007-08-03"]
> def m; yield 1, 2; end
> m{|v| v} # => 1

So sort_i needs to be revised to take two parameters, as follows:

static VALUE
sort_i(VALUE first, VALUE second)
{
long v1 = FIX2LONG(first);
long v2 = FIX2LONG(second);
return LONG2FIX(v1 - v2);
}

(Note that I have not tested this, and I have not programmed a C
extension ever without the help of SWIG.)

--Ken

--
Ken (Chanoch) Bloom. PhD candidate. Linguistic Cognition Laboratory.
Department of Computer Science. Illinois Institute of Technology.
http://www.iit.edu...