From: Daniel Martin Date: 2007-08-18T23:58:08+09:00 Subject: YAML and NArray In the past week, I've been exploring a little problem that by Wednesday I was ready to sit down and code. Now, the details of the problem aren't important, but for this problem I was going to need to have objects that I could read from a file, manipulate a few hundred times, and then write back to a file. Now, object serialization is not really what I'd consider "fun" stuff, so I figured "I'll just throw YAML at it". Unfortunately, my objects contained several NArray (and NMatrix and NVector) objects, and YAML initially serialized them as: irb(main):002:0> NArray.float(3,3).to_yaml => "--- !ruby/object:NArray {}\n\n" Not helpful. Searching the web pulled up a ruby-talk message from a few years ago, but apparently the YAML api for defining your own YAML serialization has changed since then. Although this new API appears to be undocumented (at least, I couldn't find any documentation), I did find a few blog posts in which people showed examples with other classes. So anyway, I finally integrated YAML and NArray, and present the solution here for other people googling in the future on how to get these two to work together, as well as now for comment and criticism by current list subscribers. Note that it handles NArray, NMatrix, and NVector objects: ########## narray-yaml.rb ######### require 'yaml' require 'narray' # First, a yaml utility routine to make the output prettier module YAML class YIArray < Array yaml_as "tag:yaml.org,2002:seq" def to_yaml_style; :inline; end end def YAML.inline_array(arr) if Array === arr[0] then arr.map {|s| inline_array(s)} else YIArray.new.concat(arr) end end end class NArray # I used a tag of my own since I wasn't sure if # someone else had their own serialization of # NArray objects yaml_as "tag:martin@snowplow.org,2007:narray" def to_yaml( opts = {} ) YAML::quick_emit( self.object_id, opts ) do |out| out.map(taguri) do |map| map.add('typecode', typecode) map.add('shape', YAML::inline_array(shape)) map.add('data', YAML::inline_array(to_a)) end end end def self.yaml_new(klass, tag, val) result = klass.new(val['typecode'], *(val['shape'])) result[] = val['data'] result end end __END__ The result looks like this, for a 2 by 2 by 2 NArray: --- !martin@snowplow.org,2007/narray typecode: 5 shape: [2, 2, 2] data: !seq:Array - !seq:Array - [0.441292242643088, 0.463176002629107] - [0.602508503899103, 0.155671031769272] - !seq:Array - [0.558707757356912, 0.536823997370893] - [0.397491496100897, 0.844328968230728] So I'm pretty happy with the output format, but not happy with how long it took to get there, both in terms of the amount of code and in terms of the amount of time spent tracking stuff down, and the amount of time spent looking at documentation for the wrong API trying to figure out why my code wasn't working. Any suggestions on how to clean up my code, and any pointers to documentation I missed? -- s=%q( Daniel Martin -- martin@snowplow.org puts "s=%q(#{s})",s.to_a.last ) puts "s=%q(#{s})",s.to_a.last