"number-in-base" ``oneliner''

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Alex Martelli

    #16
    Re: "number-in-base" ``oneliner''

    Bengt Richter <bokr@oz.net> wrote:
    [color=blue]
    > Not sure if I got filtered out replying to myself, what with an accidental
    > dupe and all the incremental changes and corrections ;-/ Anyway, I guess
    > you could say there's a "while" implicit in iter(f, sentinel) that you
    > _can_ do within a list comprehension ;-)[/color]

    Yep, excellent suggestion, tx. I went with the logarithm suggestion,
    but in other cases the two-arguments iter could surely be the best way
    to hide a 'while callable() != sentinel:' in a list comprehension!-)


    Alex

    Comment

    • Dan Bishop

      #17
      Re: &quot;number-in-base&quot; ``oneliner''

      Andrea Griffini <agriff@tin.i t> wrote in message news:<nl07o0lr3 dvunoq2j0avuvmf tnc837fvoc@4ax. com>...[color=blue]
      > On Fri, 29 Oct 2004 23:34:42 GMT, exarkun@divmod. com wrote:
      >[color=green]
      > > range(maxlen) can be replaced with range(int(math. log(x) / math.log(N)) + 1).[/color]
      >
      > Log accepts the base as second argument.
      >
      > def number_in_base( x, N=10, digits="0123456 789ABCDEF"):
      > return '-'[x>=0:]+"".join(
      > [digits[abs(x)/N**i%N]
      > for i in xrange(1+int(ma th.log(abs(x)+1 ,N)))
      > if N**i<=abs(x)][::-1]) or digits[0]
      >[color=green]
      > > Also, and perhaps you are already aware, number_in_base( x, 1, '0') doesn't produce the correct output with the above algorithm, although I believe it will if you switch to using math.log().[/color]
      >
      > It doesn't handle roman numerals either...[/color]

      That's easy enough to implement ;-)

      roman = lambda n: 'M' * (n // 1000) + ('', 'C', 'C', 'CCC', 'CD', 'D',
      'DC', 'DCC', 'DCCC', 'CM')[n // 100 % 10] + ('', 'X', 'XX', 'XXX',
      'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC')[n // 10 % 10] + ('', 'I', 'II',
      'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX')[n % 10]

      I'm sure there's a shorter way, though.

      Comment

      • Steven Bethard

        #18
        Re: &quot;number-in-base&quot; ``oneliner''

        Bengt Richter <bokr <at> oz.net> writes:[color=blue]
        >
        > BTW, will anything that works in a list comprehension work in a generator
        > expression (assuming one does not depend on the generator expression having
        > leftover outside side effect bindings like the LC version)?[/color]

        Well, I'm not confident enough to say *anything* (though I've used them freely
        in my code since 2.4a1 was released and have never had any problems) but they
        seem to work for this problem:
        [color=blue][color=green][color=darkred]
        >>> def number_in_base( n, b=10, digits='0123456 789ABCDEF'):[/color][/color][/color]
        .... return '-'[:n<0]+''.join(revers ed(list(
        .... digits[r]
        .... for q in [abs(n)]
        .... for q, r in iter(lambda: divmod(q, b), (0, 0))))) or digits[0]
        ....[color=blue][color=green][color=darkred]
        >>> number_in_base( 100, 16)[/color][/color][/color]
        '64'[color=blue][color=green][color=darkred]
        >>> number_in_base( 100, 2)[/color][/color][/color]
        '1100100'

        Of course, you don't really gain anything by doing this with a generator
        expression since you have to reverse the list.

        In Python 3000, the list(genexp) syntax will be exactly equivalent to a list
        comprehension[1], at which point, it won't matter. ;)

        Steve

        [1] http://www.python.org/peps/pep-3000.html#core-language

        Comment

        • Andrea Griffini

          #19
          Re: &quot;number-in-base&quot; ``oneliner''

          On 31 Oct 2004 21:12:49 -0800, danb_83@yahoo.c om (Dan Bishop) wrote:
          [color=blue]
          >roman = lambda n: 'M' * (n // 1000) + ('', 'C', 'C', 'CCC', 'CD', 'D',
          >'DC', 'DCC', 'DCCC', 'CM')[n // 100 % 10] + ('', 'X', 'XX', 'XXX',
          >'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC')[n // 10 % 10] + ('', 'I', 'II',
          >'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX')[n % 10]
          >
          >I'm sure there's a shorter way, though.[/color]

          This is a bit shorter ...

          roman = lambda x: "".join(["".join(map(lam bda c: "IVXLCDM"[ord(c) -
          ord("A")+2*N], "/A/AA/AAA/AB/B/BA/BAA/BAAA/AC".split("/")[x//10**N %
          10])) for N in (3, 2, 1, 0)])

          Andrea

          Comment

          • Andrea Griffini

            #20
            Re: &quot;number-in-base&quot; ``oneliner''

            On Mon, 01 Nov 2004 07:21:09 GMT, Andrea Griffini <agriff@tin.i t>
            wrote:
            [color=blue]
            >This is a bit shorter ...
            >
            >roman = lambda x: "".join(["".join(map(lam bda c: "IVXLCDM"[ord(c) -
            >ord("A")+2*N], "/A/AA/AAA/AB/B/BA/BAA/BAAA/AC".split("/")[x//10**N %
            >10])) for N in (3, 2, 1, 0)])[/color]

            No idea why I initially thougt about using ASCII codes... this
            is simpler and shorter...

            roman = lambda x: "".join(["".join(map(lam bda c: "IVXLCDM"[int(c)
            +2*N],"/0/00/000/01/1/10/100/1000/02".split("/")[x//10**N % 10]))
            for N in (3, 2, 1, 0)])

            Andrea

            Comment

            • Irmen de Jong

              #21
              Re: &quot;number-in-base&quot; ``oneliner''

              Andrea Griffini wrote:
              [color=blue]
              > roman = lambda x: "".join(["".join(map(lam bda c: "IVXLCDM"[int(c)
              > +2*N],"/0/00/000/01/1/10/100/1000/02".split("/")[x//10**N % 10]))
              > for N in (3, 2, 1, 0)])[/color]

              This rocks!! I'm still trying to understand how it works though :)
              --Irmen

              Comment

              • Andrea Griffini

                #22
                Re: &quot;number-in-base&quot; ``oneliner''

                On Mon, 01 Nov 2004 11:06:24 +0100, Irmen de Jong
                <irmen@-NOSPAM-REMOVETHIS-xs4all.nl> wrote:
                [color=blue]
                >Andrea Griffini wrote:
                >[color=green]
                >> roman = lambda x: "".join(["".join(map(lam bda c: "IVXLCDM"[int(c)
                >> +2*N],"/0/00/000/01/1/10/100/1000/02".split("/")[x//10**N % 10]))
                >> for N in (3, 2, 1, 0)])[/color]
                >
                >This rocks!! I'm still trying to understand how it works though :)[/color]

                The idea is to use the pattern

                "", "0", "00", "000", "01", "1", "10", "100", "1000", "02"

                where every character listed above is an offest in

                "IVX", "XLC", "CDM", "M??"

                For example applying the offsets to the first group you get:

                "", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"

                i.e. the roman numbers for 0..9; and applying it to the second

                "", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC",

                i.e. 0, 10, 20, ... 90.

                I just used "split" and string indexing instead of regular
                lists because the code becomes smaller (and more readable
                than a mess of quotes and commas).

                The rest is a loop on the thousands, the hundreds, the tens
                and the units of the number and the joins needed to glue
                all the pieces into a resulting string.

                Andrea

                Comment

                Working...