From: verec
Subject: forcing representation of denominator 1?
Date: 
Message-ID: <445d0d96$0$25453$5a6aecb4@news.aaisp.net.uk>
Hello.

I'm dealing with odds that CL offers me a very convenient way
to represent using rationals.

From time to time I need to switch representation, from
the usual bookie way, ie 5/1 means: "risk 5 for a profit of 1"
to another which states "pay 5, win 6".

This means that really 5/1 in one representation is really
the same as 5/6 in the other. So far so good.

(defun risk-reward-to-pay-win (odds)
  "Transforms a ratio such as 5/1 to 5/6, that is, from 'risk 5 and proft 1'
to 'pay 5 win 6'"
  (let ((n (numerator odds))
        (d (denominator odds)))
    (/ n (+ n d))))

(defun pay-win-to-risk-reward (odds)
  "Transfors a ratio such as Pay 5 Win 6 to a risk reward ratio such as 5/1"
  (let* ((n (numerator odds))
         (d (denominator odds))
         (k (- d n)))
    (/ n k)))

My problem is this:

CL-USER 7 > (risk-reward-to-pay-win 5/1)
5/6

CL-USER 8 > (pay-win-to-risk-reward 5/6)
5

Well, that's all mathematically true that 5/1 is 5. But I want
the ouptut to read 5/1 not 5.

CL-USER 9 > (the rational 5)
5

No luck.

Obviously, I could write:

(defun force-rational-output (x)
  (let ((n (numerator x))
        (d (denominator x)))
    (if (eql d 1)
        (format nil "~a/1" n)
      (format nil "~a" x))))

CL-USER 11 > (force-rational-output 5)
"5/1"

and be done with it. But besides being ugly, this forces
convertion to a string, which I'd rather avoid until the
last possible step.

Any idea?

Many Thanks.
--
JFB

From: Pascal Bourguignon
Subject: Re: forcing representation of denominator 1?
Date: 
Message-ID: <87vesivnzd.fsf@thalassa.informatimago.com>
verec <·····@mac.com> writes:
> My problem is this:
>
> CL-USER 7 > (risk-reward-to-pay-win 5/1)
> 5/6
>
> CL-USER 8 > (pay-win-to-risk-reward 5/6)
> 5
>
> Well, that's all mathematically true that 5/1 is 5. But I want
> the ouptut to read 5/1 not 5.
>
> CL-USER 9 > (the rational 5)
> 5
>
> No luck.
>
> Obviously, I could write:
>
> (defun force-rational-output (x)
>  (let ((n (numerator x))
>        (d (denominator x)))
>    (if (eql d 1)
>        (format nil "~a/1" n)
>      (format nil "~a" x))))
>
> CL-USER 11 > (force-rational-output 5)
> "5/1"
>
> and be done with it. But besides being ugly, this forces
> convertion to a string, which I'd rather avoid until the
> last possible step.

(denominator 5) --> 1

When you use the function pay-win-to-risk, you're working within your
"representation", and you're getting the correct results:

(values (numerator   (pay-win-to-risk-reward 5/6))
        (denominator (pay-win-to-risk-reward 5/6)))
--> 5 ;
    1

At the last step, you'll need to use force-rational-output (but I'd
call it PRINT-ODDS instead).

The REPL printer is a debugger tool, not an application user interface!


Now, what you could do, is to use a structure or a CLOS object.
Then you could define a PRINT-OBJECT method.


I've got some questions for you.  What do the following forms mean?

(+ (risk-reward-to-pay-win 5/1) (risk-reward-to-pay-win 3/2))
(* (pay-win-to-risk-reward 5/6) (pay-win-to-risk-reward 7/8))
(+ (risk-reward-to-pay-win 5/1) (pay-win-to-risk-reward 7/8))
(* (pay-win-to-risk-reward 5/6) (risk-reward-to-pay-win 3/2))
5/6 ; (Is it a pay-win, or a risk-reward?)

I don't know much of betting ratios, but I'd bet most of these forms
are meaningless.  Therefore, I strongly advise you to use a structure
or a CLOS object to represent them!  You can still use a ratio as
underlying representation, but this should be hidden.
;; Untested code follows.

(defclass risk-reward ()
  ((risk   :accessor risk   :initarg :risk)
   (reward :accessor reward :initarg :reward)))

(defclass pay-win ()
  ((pay :accessor pay :initarg :pay)
   (win :accessor win :initarg :win)))

(defmethod pay-win ((self risk-reward))
   (make-instance 'pay-win :pay (risk self)
                           :win (+ (risk self) (reward self))))

(defmethod risk-reward ((self pay-win))
   (make-instance 'risk-reward :risk (pay self)
                               :reward (- (win self) (pay self))))

If you prefer to use ratios:

(defclass risk-reward ()
  ((value)))

(defmethod risk   ((self risk-reward)) (numerator   (slot-value self 'value)))
(defmethod reward ((self risk-reward)) (denominator (slot-value self 'value)))
(defmethod (setf risk)   (val (self risk-reward))
   (setf (slot-value self 'value) (/ val (denominator (slot-value self 'value))))
   val)
(defmethod (setf reward)   (val (self risk-reward))
   (setf (slot-value self 'value) (/ (numerator (slot-value self 'value)) val))
   val)
(defmetod initialize-instance ((self 'risk-reward) &key risk reward 
                               &allow-other-keys)
   (call-next-method)
   (setf (risk   self) risk)
   (setf (reward self) reward)
   self)

and you don't need to change anything else:

(defmethod pay-win ((self risk-reward))
   (make-instance 'pay-win :pay (risk self)
                           :win (+ (risk self) (reward self))))

(defmethod risk-reward ((self pay-win))
   (make-instance 'risk-reward :risk (pay self)
                               :reward (- (win self) (pay self))))

stay the same.  You can also use ratios to implement the pay-win class...

But now, you can define print-object methods:

(defmethod print-object ((self risk-reward) stream)
  (if *print-readably*
     (format stream "#.(make-instance '~S :risk ~S :reward ~S)"
        (class-name (class-of self)) (risk self) (reward self))
     (format stream "#<Risk/Reward ~A/~A>" (risk self) (reward self))))


-- 
__Pascal Bourguignon__                     http://www.informatimago.com/

"Debugging?  Klingons do not debug! Our software does not coddle the
weak."
From: verec
Subject: Re: forcing representation of denominator 1?
Date: 
Message-ID: <445d3111$0$25454$5a6aecb4@news.aaisp.net.uk>
On 2006-05-06 22:32:06 +0100, Pascal Bourguignon <···@informatimago.com> said:

> (denominator 5) --> 1
> 
> When you use the function pay-win-to-risk, you're working within your
> "representation", and you're getting the correct results:
> 
> (values (numerator   (pay-win-to-risk-reward 5/6))
>         (denominator (pay-win-to-risk-reward 5/6)))
> --> 5 ;
>     1
> 
> At the last step, you'll need to use force-rational-output (but I'd
> call it PRINT-ODDS instead).
> 
> The REPL printer is a debugger tool, not an application user interface!

I see that :-) I was just hoping that there existed some super
secret type that only CLHS anthropologists :) could know of that
would be some kind of

rational-that-prints-its-denominator-no-matter-what

Failing that, I was expecting a *print-xyz* var to set to this or that
so as to force the printer to output denominator values of 1 ...

Tough luck :-(

> I've got some questions for you.  What do the following forms mean?
> 
> (+ (risk-reward-to-pay-win 5/1) (risk-reward-to-pay-win 3/2))
> (* (pay-win-to-risk-reward 5/6) (pay-win-to-risk-reward 7/8))
> (+ (risk-reward-to-pay-win 5/1) (pay-win-to-risk-reward 7/8))
> (* (pay-win-to-risk-reward 5/6) (risk-reward-to-pay-win 3/2))
> 5/6 ; (Is it a pay-win, or a risk-reward?)

That's not defined (and I have no intention to). As I said
I just found it _convenient_ to use rationals in this case. But
if this convenience comes at too high a price I'll reconsider.

It is convenient because you can go almost directly from a
probability measure to (yet a third kind of) ratio

(defun get-odds (p)
  "given a probability between 0 and 1 of the event E, returns
the minimum value of the odds such that a bookie will break-even"
  (rational (/ 1 p)))

(get-odds 0.5)      =>  2 (really: 2/1, ie n/m leading to m/n 1st kind
                           or n/m+n 2nd kind)
(get-odds 0.25)     =>  4 (really 4/1 ...)

> (defmethod print-object ((self risk-reward) stream)
>   (if *print-readably*
>      (format stream "#.(make-instance '~S :risk ~S :reward ~S)"
>         (class-name (class-of self)) (risk self) (reward self))
>      (format stream "#<Risk/Reward ~A/~A>" (risk self) (reward self))))

I see your point. Thanks. But that's probably overkill for now.
Anyway I just archived a copy of it :-)

Many Thanks
--
JFB
From: Christophe Rhodes
Subject: Re: forcing representation of denominator 1?
Date: 
Message-ID: <sqlkte6zr8.fsf@cam.ac.uk>
verec <·····@mac.com> writes:

> CL-USER 11 > (force-rational-output 5)
> "5/1"
>
> and be done with it. But besides being ugly, this forces
> convertion to a string, which I'd rather avoid until the
> last possible step.
>
> Any idea?

This separation between representation and I/O is the kind of thing
that CLIM's notion of presentation types is fairly good at solving:
the point is that there is often a many-to-one mapping between the
type of an application object and the type of its representation as a
Common Lisp object; the canonical example of this in the CLIM
literature is temperature, I believe, but essentially any quantity
with units will do as an example.

In your application, you would tend to use one representation for odds
as the canonical one.  In my code below I've chosen risk-reward as
canonical, for no particular good reason.  So in CLIM, what you would
do is something like

(in-package :clim-user)

(defun risk-reward-to-pay-win (odds)
 "Transforms a ratio such as 5/1 to 5/6, that is, from 'risk 5 and proft 1'
to 'pay 5 win 6'"
 (let ((n (numerator odds))
       (d (denominator odds)))
   (/ n (+ n d))))

(defun pay-win-to-risk-reward (odds)
 "Transfors a ratio such as Pay 5 Win 6 to a risk reward ratio such as 5/1"
 (let* ((n (numerator odds))
        (d (denominator odds))
        (k (- d n)))
   (/ n k)))

(defun print-odds-as-ratio (stream odds)
  (etypecase odds
    (integer (format stream "~A/1" odds))
    (ratio (format stream "~A" odds))))

(define-presentation-type odds ()
  :inherit-from 'rational)
(define-presentation-type pay-win ()
  :inherit-from 'odds)
(define-presentation-type risk-reward ()
  :inherit-from 'odds)

;;; should write ACCEPT methods too (for parsing keyboard input)
(define-presentation-method present 
    ((object rational) (type pay-win) stream view
     &key acceptably for-context-type)
  (print-odds-as-ratio stream (risk-reward-to-pay-win object)))
(define-presentation-method present 
    ((object rational) (type risk-reward) stream view
     &key acceptably for-context-type)
  (print-odds-as-ratio stream object))

giving something like
<http://www-jcsu.jesus.cam.ac.uk/~csr21/odds.png>.

In pure Common Lisp, if you had to do this, you would use a custom
pprint-dispatch-table, but of course that only works if you have very
fine-grained control over your printing environment, or if you don't
print any other rationals in the course of running your program.

Christophe
From: verec
Subject: Re: forcing representation of denominator 1?
Date: 
Message-ID: <445e2d9f$0$25450$5a6aecb4@news.aaisp.net.uk>
On 2006-05-07 08:49:15 +0100, Christophe Rhodes <·····@cam.ac.uk> said:

[...]

> giving something like
> <http://www-jcsu.jesus.cam.ac.uk/~csr21/odds.png>.

[...]

Many Thanks.
--
JFB
From: Thomas A. Russ
Subject: Re: forcing representation of denominator 1?
Date: 
Message-ID: <ymik68wqq33.fsf@sevak.isi.edu>
verec <·····@mac.com> writes:

This doesn't solve your original problem, but 

> (defun force-rational-output (x)
>   (let ((n (numerator x))
>         (d (denominator x)))
>     (if (eql d 1)
>         (format nil "~a/1" n)
>       (format nil "~a" x))))

Could be made shorter (although not necessarily simpler ;) by

(defun force-rational-output (x)
  (format nil "~:[~A~;~A/1~]" (integerp x) x))

Although I would be tempted to add a stream argument...


-- 
Thomas A. Russ,  USC/Information Sciences Institute
From: Gareth McCaughan
Subject: Re: forcing representation of denominator 1?
Date: 
Message-ID: <87ac9ppsvp.fsf@g.mccaughan.ntlworld.com>
"verec" wrote:

> On 2006-05-10 00:29:32 +0100, Gareth McCaughan
> <················@pobox.com> said:
> 
>> "verec" wrote:
>> 
>>> (defun risk-reward-to-pay-win (odds)
>>> "Transforms a ratio such as 5/1 to 5/6, that is, from 'risk 5 and proft 1'
>>> to 'pay 5 win 6'"
>>> (let ((n (numerator odds))
>>> (d (denominator odds)))
>>> (/ n (+ n d))))
>>> (defun pay-win-to-risk-reward (odds)
>>> "Transfors a ratio such as Pay 5 Win 6 to a risk reward ratio such as 5/1"
>>> (let* ((n (numerator odds))
>>> (d (denominator odds))
>>> (k (- d n)))
>>> (/ n k)))
>> Eww :-).
>> (defun risk-reward-to-pay-win (odds)
>>   (/ (1+ odds)))
>> (defun pay-win-to-risk-reward (odds)
>>   (/ (- 1 (/ odds))))
> 
> Almost :-(
> 
> CL-USER 1 > (risk-reward-to-pay-win 5/1)
> 1/6
> 
> CL-USER 2 > (pay-win-to-risk-reward 5/6)
> -5

Ahem. I meant:

(defun risk-reward-to-pay-win (odds)
  (/ (1+ (/ odds))))

(defun pay-win-to-risk-reward (odds)
  (/ (1- (/ odds))))

I blame all those confusing "n" and "d"s. :-)

-- 
Gareth McCaughan
.sig under construc
From: verec
Subject: Re: forcing representation of denominator 1?
Date: 
Message-ID: <4462f14c$0$644$5a6aecb4@news.aaisp.net.uk>
On 2006-05-10 20:49:23 +0100, Gareth McCaughan 
<················@pobox.com> said:

> Ahem. I meant:
> 
> (defun risk-reward-to-pay-win (odds)
>   (/ (1+ (/ odds))))
> 
> (defun pay-win-to-risk-reward (odds)
>   (/ (1- (/ odds))))

Nice!

Many Thanks
--
JFB