Hacker News new | past | comments | ask | show | jobs | submit login

The k solution is ,/+|(8#2)\textb

k doesn't have a right shift operator, but you don't need that, you can use the base encoding operator instead

Personally I think this is clearer than both the array-ish python and the list comp.

https://ngn.codeberg.page/k/#eJwrSa0oSbJSCs9ILFEA4gyFkoxUBbe...




I'm sitting here with the K reference manual, and I still can't decode this.

I'm wildly guessing that your approach somehow ends doing something closer to this:

    textb.bytes.flat_map{_1.digits(2)}
Which I must admit it took me embarrassingly long to think of.


If you go to the link and press 'help' you'll see some docs for ngn/k

The relevant line is

I\ encode 24 60 60\3723 -> 1 2 3 2\13 -> 1 1 0 1

So (8#2)\x encodes x in binary, with 8 positions. And because k is an array language, you don't need to do any mapping, it's automatic.

,/+|x is concat(transpose(reverse(x))) (,/ literally being 'reduce with concat')


> If you go to the link and press 'help' you'll see some docs for ngn/k

Almost as impenetrable as the code unless you already know the language. But that's ok - I guess that's the main audience...

E.g. trying to figure out what "\" means, in that help is only easier now because you gave me the whole line, as there are 64 occurrences of "\" in that doc and I wouldn't have known what pattern to search for to limit it...

It's back to the philosophical disconnect of expecting people to read start to finish/know far more detail inside out rather than skimming and relying on easy keyword lookups... (yes, we're lazy)

> 'reduce with concat'

So "flatten" in Ruby-speak, I take it (though "flatten" without an argument in Ruby will do this recursively, so I guess probably flatten(1) would be a more direct match).

> you don't need to do any mapping, it's automatic.

After these pointers (thanks!), here's - mostly for my own learning, what I ended up with not in an attempt to get closer to the linenoise (we can do that with a horrific level of operator overloading that'd break most of the standard library, though we can't match k precisely). Please don't feel obliged to go through this unless you're morbidly curious; I just had to, but I'm sure you'd suffer going through my attempt at figuring out what the hell k is doing...:

    textb="What hath the Flying Spaghetti Monster wrought?"

    # Firstly, I finally realised after a bunch of testing that 1) "(8#2)" does something like this.
     # That is, the result of 8#2 is (2 2 2 2 2 2 2 2), which was totally not what I expected.
    def reshape(len,items) = [].fill(0...x)

    class Integer
      # For the special case of (x#y) where x is a positive integer, which is frankly the only one
      # I've looked at, we can do:
      # So now 4.reshape(2) returns [2 2 2 2] just like (4#2) in ngn/k
      def reshape(items) = Array(items)*self

      # Now we can do something somewhat like what I think "encode" is
      # actually doing - this can be golfed down, but anyway:
      # With this, "a".ord.encode(8.reshape(2)) returns [0,1,1,0,0,0,0,1],
      # equivalent to (8#2)\ "a" in ngn\k
      def encode(shape)
        rem = self
        Array(shape).reverse.map do |v|
          val = rem % v
          rem = rem / v
          val
        end.reverse
      end
    end

    # Now we can break Array too.      
    class Array
      # First a minor concession to how Ruby methods even on the Array
      # class sees the focal point as the Array rather than the elements.
      # E.g. `self` in #map is the Array. If the focus is to be on applying the
      # same operation to each element, then it might be more convenient
      # if `self` was the element. With this, we can do ary.amap{reverse}
      # instead of ary.map{|e| e.reverse} or ary.map{ _1.reverse}. 
      # To get closer to k, we'd have needed a postfix operator that we could
      # override to take a block, but unfortunately there are no overridable 
      # postfix operators in Ruby. E.g. we can hackily make
      # ary.>>(some_dummy_value) {a block} work, but not even
      # ary >> (some_dummy_value) { a block} and certainly not
      # ary >> { a block }
      #
      def amap(&block) = map { _1.instance_eval(&block) }

      # If we could do a "nice" operator based map, we'd just have left it
      # at that. But to smooth over the lack of one, we can forward some
      # methods to amap:
      def encode(...) = amap{encode(...)}
      # ... with the caveat that I realised afterwards that this is almost certainly
      # horribly wrong, in that I think the k "encode" applies each step of the
      # above to each element of the array and returns a list of *columns*.
      # I haven't tried to replicate that, as it breaks my mind to think about 
      # operating on it that way. That is, [65,70].encode(2.reshape(10))
      # really ought to return [[6,7],[5,0]] to match the k, but it returns
      # [[6,5],[7,0]]. Maybe the k result will make more sense to me if I
      # take a look at how encode is implemented...

      def mreverse = amap{reverse}
    end

    # Now we can finally get back to the original, with the caveat that due to
    # the encode() difference, the "mreverse.flatten(1)" step is in actuality
    # working quite differently, in that for starters it's not transposing the arrays.
    #
    p textb.bytes.encode(8.reshape(2)).mreverse.flatten(1)

    # So to sum up:
    #
    # textb            ->   textb.bytes since strings and byte arrays are distinct in Ruby
    # (8#2)           ->   8.reshape(2)
    # x\y               ->   y.encode(x) ... but transposed.
    # |x                ->   x.mreverse
    # ,/+x             ->   x.flatten(1)   .. but really should be x.transpose.flatten(1)
    #
    # Of course with a hell of a lot of type combinations and other cases the k
    # verbs supports that I haven't tried to copy.


i can't decode it either but i think you're right. note that this probably gives the bits in big-endian order rather than little-endian order like my original, but for my purposes in that notebook either one would be fine as long as i'm consistent encoding and decoding


I got a few steps further after lots of experimentation with ngn/k and the extra hints further downthread:

https://gist.github.com/vidarh/3cd1e200458758f3d58c88add0581...

The big caveat being it clicked too late that 1) "encode" is not "change base and format", but "for each element in this array, apply the module to the entire other array, and pass the quotient forward", and 2) encode returns a list of columns of the remainders rather than rows (the output format really does not make this clear...).

So you can turn a list of seconds into hour, minute, seconds with e.g.: (24 60 60)\(86399 0 60), but what you get out is [hour, minute, second] where hour, minute, second each are arrays.

If you want them in the kind of format that doesn't break the minds of non-array-thinking people like us because the order actually matches the input, you'd then transpose them by prepending "+", because why not overload unary plus to change the structure of the data?

   +(24 60 60)\(86399 0 60)
Returns (23 59 59 0 0 0 0 1 0)

Or [[23,59,59], [0,0,0], [0,1,0]] in a saner output format that makes it clear to casuals like me which structure is contained in what.

Now, if you then want to also flatten them, you prepend ",/"

I feel much better now. Until the next time I spend hours figuring out a single line of k.


It would do if it the bits weren't reversed, which is done with |


thank you for the correction! and the explanation


thank you very much! which k implementation does this page use?

oh, apparently a new one called ngn/k: https://codeberg.org/ngn/k


As much as it's a struggle to read, you got to love the people implementing this are dedicated enough to write their C as if it was K.




Consider applying for YC's first-ever Fall batch! Applications are open till Aug 27.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: