🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Thread View: pl.comp.lang.ruby
8 messages
8 total messages Started by Rafał Komorowski Sat, 07 Oct 2006 19:58
Jak dodać własną metodę typu nazwa! do klasy?
#219
Author: Rafał Komorowski
Date: Sat, 07 Oct 2006 19:58
33 lines
1644 bytes
Nie spodziewam się pozytywnej odpowiedzi na poniższe pytanie, ale wolę 
spróbować. Chcę rozszerzyć funkcjonalność klasy Float o kilka 
brakujących metod.

Działa mi takie coś:

	class Float
	 	# zaokrągla liczbę typu Float do jednego miejsca po przecinku
		def round01
			(self*10).round / 10.0
		end
	end

I fajnie. Ale marzy mi się napisanie także metody round01!, która 
zaokrągla liczbę "in place". Żeby można było napisać:
	a=3.1415
	a.round01!

Zdaje się, że jest to niemożliwe, bo klasa Float nie jest 
zaimplementowana w Rubym. Próba typu:

	def round01!
		self = (self*10).round / 10.0
	end

kończy się porażką, ponieważ wartość self jest tylko do odczytu. Czy 
dobrze myślę, że nie da się tego zrobić  bez grzebania w implementacji 
klasy Float?



-- 
komor_newsNO@macSPAM.com
Re: Jak dodać własną metodę typu nazwa! do klasy?
#220
Author: Marcin Mielżyński
Date: Sat, 07 Oct 2006 21:38
34 lines
1644 bytes
Tutaj chodzi chyba spójność z Fixnumem. Pomimo że Float nie jest 
immediate value - jest immutable. Choć z definicji:

struct RFloat {
     struct RBasic basic;
     double value;
};


wynikałoby że jest mutable.


najmniej można zrobić z Fixnumami bo jego instancje to poprostu 4 (lub 8 
na 64 bitowych platformach) bajtów w pamięci z ustawionym najmłodszym 
bitem - więc nie mają składowej basic a przez to nie mają składowej 
klass, ...wogóle nic nie mają i nie można zrobić np tak:


class << 1
end

: no virtual class for Fixnum (TypeError)



tutaj jest algorytm dekodowania typów Rubiego:

http://www.oreillynet.com/ruby/blog/2006/01/the_ruby_value_1.html



...Najlepiej zrób kompozycje.

lopex
Re: Jak dodać własną metodę typu nazwa! do klasy?
#221
Author: Rafał Komorowski
Date: Sat, 07 Oct 2006 23:25
41 lines
2357 bytes
On 2006-10-07 23:38:50 +0200, Marcin Mielżyński <lopx@gazeta.pl> said:

> Tutaj chodzi chyba spójność z Fixnumem. Pomimo że Float nie jest 
> immediate value - jest immutable.

Dzięki, wydaje mi się, że zrozumiałem :-)

> tutaj jest algorytm dekodowania typów Rubiego:
> http://www.oreillynet.com/ruby/blog/2006/01/the_ruby_value_1.html

Bardzo ciekawy artykulik. Dobrze, że coraz więcej jest bardziej 
zaawansowanych materiałów o Rubym, idziemy do przodu! :-)

> ...Najlepiej zrób kompozycje.

Co masz na myśli? "aggregation" w sensie railsowym?

A tak ogólnie, w klasach wbudowanych w Rubiego bardzo brakuje mi paru 
dość oczywistych funkcji, w tym właśnie zaokrąglania floata do zadanego 
miejsca po przecinku. Szkoda takie podstawowe rzeczy implementować już 
w interpretowanym kodzie, do tego przecież właśnie dobrze nadaje się 
kompilowane C.
Ponadto chciałbym też mieć sprintf, które generuje przecinek jako 
decimal-point i kropkę jako thousands-separator, ale to już z innej 
beczki. Z braku lepszego pomysłu mam po prostu:

  class Fixnum
    def format00
      sprintf("%d", self).gsub(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1.")
    end
  end

ale serce mi się kraje, jak widzę wyrażenia regularne zastosowane do 
tak prymitywnych rzeczy. Może jednak warto nauczyć się robić wstawkę w 
C do kodu Rubiego? Nie interesowałem się tym na razie, ale widzę, że w 
"biblii" jest to szeroko opisane.



-- 
komor_newsNO@macSPAM.com
Re: Jak dodać własną metodę typu nazwa! do klasy?
#222
Author: Marcin Mielżyński
Date: Sun, 08 Oct 2006 00:04
97 lines
3615 bytes
Rafał Komorowski wrote:
> Bardzo ciekawy artykulik. Dobrze, że coraz więcej jest bardziej 
> zaawansowanych materiałów o Rubym, idziemy do przodu! :-)
> 

najlepszy materiał to http://rhg.rubyforge.org/ - szkoda że tylko część 
jest przetłumaczona na angielski, plus oczywiście źródła.


na szybko zrobiłem rozszerzenie z tą metodą round01 - działa bez problemu:

mutfloat.c:

#include "ruby.h"

static VALUE
round01(num)
     VALUE num;
{
    double val = RFLOAT(num)->value;
    if (val > 0.0) val = floor(val+0.5);
    if (val < 0.0) val = ceil(val-0.5);

    RFLOAT(num)->value = val;

     return num;
}

void Init_Mutfloat(){
     rb_define_method(rb_cFloat, "round01!",  round01, 0);
}

gcc -shared mutfloat.c -lruby -o MutFloat.so

można też wcisnać do numeric.c - będzie wtedy pełen built-in

require 'Mutfloat.so' # to jeśli nie built-in

b = a = 1.3
p a.round01!
p b

i tego właśnie Matz chciał pewnie uniknąć...

Druga sprawa - patrzę na źródła 1.9 - RFloat jeszcze tam jest - ale może 
się zdarzyć że pewnego dnia FLoat stanie się immediate i nie będzie już 
żadnych sztuczek (np: rezerwacja 3 bitu z VALUE). W tej chwili Floaty w 
Rubym są bardzo kosztowne niewiele dając w zamian.



>> ...Najlepiej zrób kompozycje.
> 
> Co masz na myśli? "aggregation" w sensie railsowym?
> 
> A tak ogólnie, w klasach wbudowanych w Rubiego bardzo brakuje mi paru 
> dość oczywistych funkcji, w tym właśnie zaokrąglania floata do zadanego 
> miejsca po przecinku. Szkoda takie podstawowe rzeczy implementować już w 
> interpretowanym kodzie, do tego przecież właśnie dobrze nadaje się 
> kompilowane C.
> Ponadto chciałbym też mieć sprintf, które generuje przecinek jako 
> decimal-point i kropkę jako thousands-separator, ale to już z innej 
> beczki. Z braku lepszego pomysłu mam po prostu:
> 
>  class Fixnum
>    def format00
>      sprintf("%d", self).gsub(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1.")
>    end
>  end
> 
> ale serce mi się kraje, jak widzę wyrażenia regularne zastosowane do tak 
> prymitywnych rzeczy. Może jednak warto nauczyć się robić wstawkę w C do 
> kodu Rubiego? Nie interesowałem się tym na razie, ale widzę, że w 
> "biblii" jest to szeroko opisane.
> 
> 

wszystko jest ;)

puts "%.3f" % 12.5234

z wypełnionymi spacjami: "%10.3f" % 12.5234
i wyrównane do lewej: "%-10.3f" % 12.5234

jest do tego pełna dokumentacja String#sprintf i String#%
albo po prostu:

man sprintf

jest też fajny man online:

http://man.splitbrain.org/sprintf


po co wymyślać koło....

lopex
Re: Jak dodać własną metodę typu nazwa! do klasy?
#223
Author: Rafał Komorowski
Date: Sun, 08 Oct 2006 12:48
69 lines
3658 bytes
On 2006-10-08 02:04:00 +0200, Marcin Mielżyński <lopx@gazeta.pl> said:

> na szybko zrobiłem rozszerzenie z tą metodą round01 - działa bez problemu:

Jesteś niesamowity :-) Dzięki, spróbuję to wsadzić do swojego kodu. 
Oczywiście takie wstawki mają tę wadę, że trzeba to też wpychać na 
serwery produkcyjne, co może być problematyczne, jeśli nie ma się 
pełnej kontroli nad takim serwerem, ale na razie ten problem mi nie 
grozi.

> b = a = 1.3
> p a.round01!
> p b
> 
> i tego właśnie Matz chciał pewnie uniknąć...

Co masz na myśli? Zmianę prostej wartości "w miejscu"? Z tego co widzę, 
funkcje z wykrzyknikiem są rzeczywiście zdefiniowane raczej do bardziej 
skomplikowanych struktur, ja się nie upieram, żeby w Rubiego koniecznie 
była wbudowana wersja "!", ale sama potrzeba istnienia zaokrąglania do 
określonej cyfry po przecinku jest - i nie ma co z tym dyskutować. Jak 
nie będzie tego w języku, to będzie trzeba to robić w kodzie Ruby, co 
będzie bardziej kosztowne.
Patrzę jeszcze raz na zacytowany powyżej kod i nie jestem pewien czy 
się zrozumieliśmy, moja funkcja ma robić takie coś:

3.1415.round01	=> 3.1
3.1615.round01	=> 3.2

(pomijam problem wykrzyknika). Oczywiście gdyby takie coś miało 
trafić do języka, to musiałoby być bardziej ogólne, czyli np. 
przyjmować argument decydujący o ilości miejsc po przecinku.

3.1465.round(3)	=> 3.147

> Druga sprawa - patrzę na źródła 1.9 - RFloat jeszcze tam jest - ale 
> może się zdarzyć że pewnego dnia FLoat stanie się immediate i nie 
> będzie już żadnych sztuczek (np: rezerwacja 3 bitu z VALUE). W tej 
> chwili Floaty w Rubym są bardzo kosztowne niewiele dając w zamian.

Nie rozumiem. Uważasz, że float powinien zniknąć jako struktura danych? 
Czy chodzi ci tylko o ryzyko rozszerzania klasy Float wstawkami w C?

>> ale serce mi się kraje, jak widzę wyrażenia regularne zastosowane do 
>> tak prymitywnych rzeczy. Może jednak warto nauczyć się robić wstawkę w 
>> C do kodu Rubiego? Nie interesowałem się tym na razie, ale widzę, że w 
>> "biblii" jest to szeroko opisane.
> 
> wszystko jest ;)
> 
> puts "%.3f" % 12.5234

Chyba się nie zrozumieliśmy. Mi chodzi o to, żeby zgodnie z zasadami 
języka polskiego (i nie tylko) _przecinek_ robił za "decimal-point", a 
_kropka_ jako "thousands-separator", czyli dokładnie odwrotnie niż w 
języku angielskim. Żeby zamiast formatowania typu:
	12,345,678.99 $

otrzymać w prosty sposób:
	12.345.678,99 zł

:-)
Czy to jest możliwe w standardowym sprintf? Z tego co przejrzałem to 
ani w C ani w Rubym nie ma takiej możliwości wprost?



-- 
komor_newsNO@macSPAM.com
Re: Jak dodać własną metodę typu nazwa! do klasy?
#225
Author: Wojciech Piekutowski
Date: Sun, 08 Oct 2006 14:56
37 lines
2491 bytes
Hej,

Rafał Komorowski wrote:
> 
> A tak ogólnie, w klasach wbudowanych w Rubiego bardzo brakuje mi paru 
> dość oczywistych funkcji, w tym właśnie zaokrąglania floata do zadanego 
> miejsca po przecinku. Szkoda takie podstawowe rzeczy implementować już w 
> interpretowanym kodzie, do tego przecież właśnie dobrze nadaje się 
> kompilowane C.
> Ponadto chciałbym też mieć sprintf, które generuje przecinek jako 
> decimal-point i kropkę jako thousands-separator, ale to już z innej 
> beczki. Z braku lepszego pomysłu mam po prostu:
> 
>  class Fixnum
>    def format00
>      sprintf("%d", self).gsub(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1.")
>    end
>  end
> 
> ale serce mi się kraje, jak widzę wyrażenia regularne zastosowane do tak 
> prymitywnych rzeczy. Może jednak warto nauczyć się robić wstawkę w C do 
> kodu Rubiego? Nie interesowałem się tym na razie, ale widzę, że w 
> "biblii" jest to szeroko opisane.
> 

Z ciekawości zajrzałem do mana:
http://unixhelp.ed.ac.uk/CGI/man-cgi?sprintf+3

Jest tam info, że generowanie tych znaków zależy od ustawienia LOCALE na 
odpowiednią wartość. W Twoim przypadku pl_PL lub pl_PL.UTF8 (jeśli 
używasz unicode w systemie). Można też zmienić tylko poszczególne 
składowe pakietu lokalizacyjnego - sam znak separacji i grupowania. 
Więcej informacji znajdziesz tutaj:
http://linuxreviews.org/man/locale/index.html.pl

Pozdrawiam,
Wojciech Piekutowski
Re: Jak dodać własną metodę typu nazwa! do klasy?
#224
Author: Marcin Mielżyński
Date: Sun, 08 Oct 2006 18:20
161 lines
5435 bytes
Rafał Komorowski wrote:

> 
>> b = a = 1.3
>> p a.round01!
>> p b
>>
>> i tego właśnie Matz chciał pewnie uniknąć...
> 
> Co masz na myśli? Zmianę prostej wartości "w miejscu"? Z tego co widzę, 
> funkcje z wykrzyknikiem są rzeczywiście zdefiniowane raczej do bardziej 
> skomplikowanych struktur, ja się nie upieram, żeby w Rubiego koniecznie 
> była wbudowana wersja "!", ale sama potrzeba istnienia zaokrąglania do 
> określonej cyfry po przecinku jest - i nie ma co z tym dyskutować. Jak 
> nie będzie tego w języku, to będzie trzeba to robić w kodzie Ruby, co 
> będzie bardziej kosztowne.

Chodzi o spójność z zachowaniem innych prostych typów jak np Fixnum, tak 
czy siak, Float jest zrobiony immutable zupełnie sztucznie - powód j.w. ;)
Jeśli będzie mutable i zrobisz round! - nagle zmieni się wartość jakiejś 
zmiennej w zupełnie innym miejscu programu - i szukaj wiartu w polu...

> Patrzę jeszcze raz na zacytowany powyżej kod i nie jestem pewien czy się 
> zrozumieliśmy, moja funkcja ma robić takie coś:
> 
> 3.1415.round01    => 3.1
> 3.1615.round01    => 3.2
> 

Opps.. było późno... Tu jest kod który to robi plus funkcja roundx która
jest sparametryzowana oraz ma wartość domyślną 10.0:


#include "ruby.h"

static VALUE
round01(num)
     VALUE num;
{
    double val = RFLOAT(num)->value;

    val = round(val * 10.0) / 10.0;

    RFLOAT(num)->value = val;

     return num;
}

static VALUE
roundx(argc, argv, a)
     // zmienna liczba argumentów
     int argc;
     VALUE *argv;
     // i nasz self
     VALUE a;
{
     // tutaj rozpakujemy pierwszy argument
     VALUE b;

     double rnd;

     rb_scan_args(argc, argv, "01", &b);

     // niekonieczne... ale dobry przykład sprawdzania typu argumentu
     // ten test to w zasadzie: long(b) & 1
	if (!FIXNUM_P(b)) {
	    rb_raise(rb_eArgError, "arg must be a Fixnum");
	}

     // jeśli nie ma argumentów
     if (argc == 0) {
         // wartość domyślna
         rnd = 10.0;
     // można też sprawdzić na argument 1 i wywalić błąd gdy != 1
     }else {
         rnd = NUM2INT(b);
     }

     double val = RFLOAT(a)->value;

     val = round(val * rnd) / rnd;

     RFLOAT(a)->value = val;

     return a;
}

void Init_Mutfloat(){
     rb_define_method(rb_cFloat, "round01!",  round01, 0);
     rb_define_method(rb_cFloat, "round!",  roundx, -1);
}


> 
>> Druga sprawa - patrzę na źródła 1.9 - RFloat jeszcze tam jest - ale 
>> może się zdarzyć że pewnego dnia FLoat stanie się immediate i nie 
>> będzie już żadnych sztuczek (np: rezerwacja 3 bitu z VALUE). W tej 
>> chwili Floaty w Rubym są bardzo kosztowne niewiele dając w zamian.
> 
> Nie rozumiem. Uważasz, że float powinien zniknąć jako struktura danych? 
> Czy chodzi ci tylko o ryzyko rozszerzania klasy Float wstawkami w C?
> 

Nie. Float powinien być immediate value, tylko że na razie brakuje 
bitów, już i tak Fixnum ma tylko 30 bitów:

(2**29).class -> Fixnum
(2**30).class -> Bignum

jeden bit stracony na flagę Fixnum, drugi bit stracony na mark&sweep
wstępnie polecam:
http://en.wikipedia.org/wiki/Garbage_collection_%28computer_science%29


W architekturach 64 bitowych nie jest to tak bolesne...

Każda immediate value ma tylko _jedną_ wersję w całym interpreterze co 
oznacza że jest tylko jedna false, true, nil, 1, 2, 3, itd. Jeżeli 
odwołujesz się do instancji 1 - zawsze odwołujesz się do tej samej 
instancji niezależnie od miejsca programu!

p 1.object_id
p 1.object_id
p 1.object_id

p 1.1.object_id
p 1.1.object_id
p 1.1.object_id

daje:

3
3
3
21103790
21103710
21103630

Tak więc, immediate values wogóle nie rządzą się prawami rządzącymi 
referencjami - kopiowana jest tak na prawdę sama zmienna a nie wartość. 
Gdybyśmy zmienili wartość 1 na 4 - nagle wszystkie 1-dynki w skali 
całego interpretera stałyby się 4-kami!.

Podobne rozwiązanie panuje w javie gdzie np: dla java.lang.Integer 
wartości od -127 do 128 są w puli - zawsze będą miały takie same 
referencje, to samo dla false, true, i całego byte'a, chara w zakresie 
00..7F.
Tylko to grozi taki niebezpieczestwem:

Integer i=100;
Integer i2=100;

i==i2 -> true

Integer i=256;
Integer i2=256;

i==i2 już niekoniecznie!


lopex
Re: Jak dodać własną metodę typu nazwa! do klasy?
#226
Author: Rafał Komorowski
Date: Sun, 08 Oct 2006 20:03
21 lines
1617 bytes
On 2006-10-08 16:56:05 +0200, Wojciech Piekutowski 
<w.piekutowski@gmail.com> said:

> Z ciekawości zajrzałem do mana:
> http://unixhelp.ed.ac.uk/CGI/man-cgi?sprintf+3
> 
> Jest tam info, że generowanie tych znaków zależy od ustawienia LOCALE 
> na odpowiednią wartość.

Hm... A co jak się nie używa Linuxa? :-)
Fajnie byłoby, gdyby jednak Ruby zachował w miarę pełną przenośność, 
niezależnie od platformy.
Skoro już w tym wątku pofolgowaliśmy sobie i piszemy (tzn. Lopex pisze) 
rozszerzenia Rubiego w C, to można też lekko zhakować sobie źródło do 
Kernel::sprintf   :-)




-- 
komor_newsNO@macSPAM.com
Thread Navigation

This is a paginated view of messages in the thread with full content displayed inline.

Messages are displayed in chronological order, with the original post highlighted in green.

Use pagination controls to navigate through all messages in large threads.

Back to All Threads