Books Review

December 28, 2012

Finishing up blog posts from this last year; things I started, but never finished.

I’ve read two books that I ought to write a short review of.  One written by an acquaintance — a colleague of my wife’s.  Ashes, Ashes.  I have two overall reactions: first, it’s quite a long novel for a debut, but parts of it are quite well written — esp. where Darrell writes to his strength (his psychological profiles and descriptions are believable, vivid and still readable); there are points where the writing could use finer editing.  Second, I recall thinking at the end that there was a large aspect of deus ex machina — and this critique from someone who embraces the supernatural.

But speaking of the Supernatural, the second book is Heaven is For Real.  The true story of a boy who saw Heaven and returned to tell the story.

The second book is touching, and endearing, and had all the more impact on me that it was written from the perspective of the dad; and it rings true to me all the more because he admits times when he made mistakes and is honest about all the things he doesn’t understand.

So, the first is interesting, but long; I’m curious to read the sequel, and then I’ll probably have a different opinion.  I give it three stars of five, but know that it does not “end” as I’d like — and, in some sense, it doesn’t end.  But I don’t want to give away the end.  There are definitely parts, and a lot of them, that I’d emphasize, and they make great conversation starters, and the overall arch of the story is great.

And both stories — maybe a little more the second — make me want to sit down and have a talk with the author(s): ask questions, find out more, write a guide for readers…

(You may be able to tell that this post is quite old, and the stories have undoubtedly circulated, but if you haven’t yet read either or both, it’s still worth doing; neither story is specifically “dated” and both can be fresh at any time.)

Haskell Sudoku

December 22, 2012

This post is incredibly technical – it is software engineer at a deep level.  I’m not sorry.

I have a working Haskell program to solve Sudoku puzzles; right now, the board name is hard-coded, but I’m at least reading from a file, and I am quite sure there are boards I cannot solve – given my method is incomplete.
But next I am going to work on a program to run tests fr. outside Haskell, and other improvements.

The code is 17 lines in the main file (plus forty lines of “test” code); and SBoard.hs is 261 lines total. Finally, 24 lines of file reading: one main function, one sub-function and a one-liner for typed Integer square root of an Int

*BoardIO> :t sqrtInt
sqrtInt :: (Integral b, Integral a) => a -> b

(I’m no longer sure why I did that, but here’s the actual code:

sqrtInt i = floor $ sqrtFloat $ 1.0 * (fromIntegral i)

Now then, the primary function is:

main =
    do
      b <- doRead "n.sdk"
      -- print b
      -- return (solve b)
      solv_dbg b

As you can see, I orignally called solve, which takes a Board as input and returns a Board, but since main has an IO monad, I needed the return there — which has nothing to do with “return” as most programmers know it and love it.

Sans putStrLn and print expressions, here is my main function for solving:

solve b =
  let b1 = calcUniqs b
       b2 = calcOpen b1
  in
   if b2 == b
      then b
     else solve b2

calcUniqs

To examine the function to calculate “unique” cells (by that I mean cells where only one value is posssible – being forced by being unique), first I need to example this utility function

{- 
    calculate a cell - if it's already known, done; 
         if there's only one possibility, done
    otherwise, it's zero for now
 -}
calc_cell c =
  let d = fst c
       u = snd c
   in
     if d > 0
        then d
       else if length u == 1
               then head u
              else 0

This is used in the “final” expression, below.  Given tuples (a, [a]) wehere a is an Ord and also a Num (due to its usage in the function); the first is the current value of the cell (zero = blank), and the second part is the list of “possible” values for this cell — it’s possible, given the calculation of this list, that there may be multiple values even though the cell is already solved, but this function doesn’t care.  The logic is,

If this “cell” is already calculated, then we’re done, return that value; else, if the list (of possible values) length is 1, then that is the new value; otherwise, the value is (still) unknown/blank, so return zero.

I should explain, this “fell out” naturally from the way I decided to encode the Board itself, and also the resulting possible values and so on.

type Symbols = [Char]
data Board = B Symbols Int [String]

A Board is, initially, a list of symbols (identical to a string, by the way), an integer which is the square root of symbols (for a 9×9 board, this is 3), then a list of Strings (technically, I ought to make that a list of symbols, I suppose…)

One very important helper function converts the list of strings — each of which is one row — into a list of lists of integers;

“toSolve”

The following is the “heart” of most of the calcs that follow (and precede)

{-
   convert a board into an array of "cells,"
   where each cell is (d, [Ints]) - if d is zero, the cell is open
   the list of integers represents the possible* values there
   (* note that is not true if d is non-zero, but in that case, we don't care)
-}
toSolve b =
    let
        rs = arrFromBoard b
        cs = transpose rs
        ps = patches b
        unf_r = map avail rs
        unf_c = map avail cs
        unf_p = map avail ps
        sq = square b
        unf = map (\x -> map (intersect x) unf_c) unf_r
        -- the following creates an "array" (list of lists)
        -- which is the same dimensions as arrFromBoard
        idx_p = map (\r -> map (\c -> sq * (sect r sq) + (sect c sq))
                           [0..(sq*sq-1)]) [0..(sq*sq-1)]
        cmplx = map (\r -> zip (fst r) (snd r)) $ zip unf idx_p
        unf2 = map (\r -> map (\i -> intersect (fst i) (unf_p !! snd i) ) r) cmplx
    in 
        map (\r -> zip (fst r) (snd r)) $ zip rs unf2

From the inside out, the underlined map takes a list (e.g. “[0..8]“) of column indicies and returns a list of indicies of the patch for this “cell” (r, [0..8]).  That map is within a func. mapped over a list of row indicies

At the end of this, idx_p is a list of lists which represents the patch index of the corresponding cell in the board array.

So, the let section of this calculates the rows (rs), then the columns (cs), then the patches (ps) — my term for the sub-squares, 3×3 on a 9×9 board; the remaining values per row (unf_r), column (unf_c) and patch (unf_c).  Next, unf represents the “intersection” of remaining values per row and column. 

Now “cmpx” is an array of (p, [Int]) tuples – same dimensions as the Sudoku board — twice is use the “pattern”

   map (\r (zip (fst r) (snd r)) $ zip <something> <else>

Where <something> and <else> represent matching arrays; in one case, “elements” of <something>” are lists, in the other, elements of <else> are lists.   The result is an array of tuples (s_i, e_i) where each “s” and each “e” are an element of <something> and <else> respectively.
In both cases, <something> and <else> are lists of lists of elements – either [[a]], or [[[a]]]; in the easier case, a list of rows, where each row is a list of columns, where each column is a single value (an Int, say) which is the value of a cell; in the more complex case, a list of rows, of columns, where each column ‘value’ is itself a list of some data representing a list of possible values, for instance, for this cell.

… and that is perhaps the most complex description I’ll write.  (I only hope that, should I come back to read this later, it will still make sense.)

Finally

calcOpen b =
  let syms = symbols b
   in
   --  the following assumes that the blank space (" ") is not a symbol
   B syms (square b) $ arrToSyms (map (\r -> map calc_cell r) (toSolve b)) syms ' '

So this calls toSolve on b, the Board, then “map (\r -> map…” because it’s a list of lists calls calc_cell on each element — and that’s why calc_cell takes the input it does.

The function toSolve takes a Board as input, as we’ve seen, and returns an array [a list of lists] of cells, where each cell is a tuple, which can be input to calc_cell far above.

After the map-map, the result is an array of new values, zero thru nine for a 9×9 board, and arrToSyms <array> syms ' ' converts that back to the [String] data to store in the Board.  “B...” is the board constructor.

show

The following code, in three functions, allows me to (pretty) print a Board

rowToStr r s =
 
  if length r > s 

     then show (take s r) ++ "  " ++ (rowToStr (drop s r) s)

    else show r



extraRow a s =

  if length a > s

     then (take s a) ++ [[]] ++ (extraRow (drop s a) s)

    else a



instance Show Board where

  show b =
 
    let s = square b
 
        rows = map (\r -> rowToStr r s) $ arrFromBoard b

     in unlines $ extraRow rows s

The magic, which I ought to remember, is in “instance Show Board where\n show…”

The function takes a Board [type] and returns a string.

 

What I’ve learned

There’s a lot to write here, but I’ve started to get the “hang” of monds — which I’ll have to write later, but others have done better.

Most important, for now, is this: the book Real World Haskell recommends that “beginners” should write the type signature first, to validate the code they get is what they intended.  I, however, took only half that advise — I thought carefully about what I wanted, but coded the function then checked the result, without including the “declaration.”  (I think I’d get the book, if I can find an updated version; there is a lot more in there than I know, yet.)

 

 

Pie in the face

November 16, 2012

Ryan was here.

A Conversation about Apps

February 1, 2012

I wrote this, literally, last year, and never published it.  Sorry it’s so late.

It started with a tweet from a friend (Siri can access Best Buy’s product catalog thru Wolfram Alpha), which encouraged me quite a bit after reading this downer (“Siri Is Apple’s Broken Promise“).

As is my wont, I responded, “I see this happening more and more (call it “app hyper linking”); I’m doing it more and more. Kids today will think no more of –”  And also, an example (Siri and Watson, or Cyc [or, now that I think of it, Wikipedia!]).

But I’m not sure Mark got that I only used Kinect as one “fer instance.”

Or maybe he did, and linked to another thought.  Then more:

“Trick is to figure out a way to get synergy from hi-res display, simultaneous site/app access and standard api for data exchange”

“I think we understand data interactions pretty well. The next breakthrough requires increased density and scale of visualization…”

I didn’t realize the full import of what that meant till now; I replied in a different direction.  Since we were talking (initially) about Siri, and I still think there’s some future to voice UI.

Then Mark added more, different thoughts (for him, the main UI of the most interesting UI of ‘the future’ is Kinect).

But I got stuck on the data thing.  Was I wrong?  I added a ref. to a story I never fully read, until now.  Here’s the conclusion from that article:

The field of astronomy is starting to generate more data than can be managed, served and processed by current techniques. This paper has outlined practices for developing next-generation tools and techniques for surviving this data tsunami, including rigorous evaluation of new technologies, partnerships between astronomers and computer scientists, and training of scientists in high-end software engineering engineering skills.

But back to what we were saying… what were we saying?

I started on the subject of “app-linking.”  The best example I have, simple though it be, is a link in my (work) e-mail to a WebEx.  From the mail app, I click the link, which switches to Safari, which somehow recognizes this is not a normal web page and launches the WebEx app.  People have complained of the lack of integration in Siri (I expect more from Apple in the months and years to come).  I remember the first time I had a full multi-OS/multi-platform experience: an old Mac iBook, running Linux, running VMWare, connected to a Windows virtual machine (it looked like Windows, felt like Linux, and had the shell of Apple).  I think this type of cross-ness will only increase, to the point where kids today won’t give a hoot (most of the time) about what “platform” their on.  Except maybe the specs of the display — and then only to figure out how to configure three “monitors” (including a wall-projector and/or a multi-panel configured LCD), and a wireless mic/headset to a back-room server of ginormous proportions [running Windows, Mac and Linux respectively] — and “apps” running in the monitor itself, communicating flawlessly with “traditional” applications, and data-manipulating back-ends software I haven’t even thought of, yet.

Our other main tangent was UIs — I like voice, Mark favors Kinect.  I think the computer from Star Trek is not that far away; but gesture-based interaction is coming nearer, too.

Haskell Trail for Sudoku

November 4, 2011

I’m going to document my “experiments” of learning Haskell, in the process of trying to write a Sudoku program — I know, I know, everyone does.  It’s not that I know more, or better, but as an educational path.

I need to specify up front that much of the code that follows is not intended as good examples, but it is what I’ve written; in fact, some of it will be decidedly bad, but I’m being more honest, and trying to learn.  If you, my friend, find a place where I’m off, wrong, and things are missing, please let me know.

Here, for example, is a “wasted” function

rdLines handle =
   Prelude.catch (
        do
            ln <- hGetLine handle
            rest <- rdLines handle
            -- this can't be the best way to accomplish that !!
            return (ln : rest) )
       (\e -> if isEOFError e then return [] else ioError e)

What I wanted, even though I am on the wrong path, is “return ln : rdLines handle” in the proper syntax.

Here’s my main function, so far:

{-
    given a filename, read the Sudoku board therein
    the format is: one line of the n "symbols" -- where n = s^2 for some integer s
    then n lines of n characters each
    return a board as defined above
 -}
doRead filename =
    bracket (openFile filename ReadMode) hClose
        (\h -> do
            syms <- hGetLine h
            putStrLn ("The first line length (of symbols) is " ++ (show (length syms)))
            let size = sqrtInt (length syms) in do
                lns <- rdLines h
                return (B syms (toInteger size) lns))

The nice function, giving the main structure, is bracket, which takes a first function, passing the result to the third function; and the second function is invoked whether the last succeeded or not, when it is finished.

So we open a file,  passing the result of that function (a handle) to the third function, hGetLine, debug that, the sqrtInt func. is my own creation, below, then read the remaining lines.  Better would be:

doRead filename =
    bracket (openFile filename ReadMode) hClose
        (\h -> do
            syms <- hGetLine h
            putStrLn ("The first line length (of symbols) is " ++ (show (length syms)))
            let size = sqrtInt (length syms) in do
                lns <- hGetContents h
                return (B syms (toInteger size) (lines lns)))

Cleaner, no?  That came to me in my sleep; I thought about what it is I am trying to accomplish, rather than how to do it, which was distracting me yesterday.

-- this is way too much work for that
sqrtInt i = floor (sqrtFloat (1.0 * (fromIntegral i)))

Is this really all necessary to calculate the integer (Int, if you want to be technical) square root of an integer [Int]?

I do hope some Haskell hacker, or more than one, reads this — some “help” would be nice learning; and something beyond LYaHGG [Learn You a Haskell Great Good]; that and a few others are good resources, like Real World Haskell, and probably YAHT.

What I learned from yesterday/today: Haskell is not well learned while I’m over-tired; I can’t think straight, and this more than anything else requires straight, rigorous thinking!

Commentary and Updates to my Clojure Code

August 13, 2011

So I realize my previous post was less descriptive than it could’ve been.  And it still will be, but here is the story.

The “project” I’m working on is to open a Fiddler session archive — that tool captures HTTP traffic, and can save the results…

The session archive I am interested is Tracer results from PegaSystems PRPC tools (PegaRules Process Commander), which is a series of XML results from the server (requested by the IE window open on my desktop requesting such).

So unless you’re extracting XML from a Fiddler session archive, this code won’t, by itself, be meaningful to you, but a smart programmer could adapt some of this for various purposes.  And, hopefully, the concepts may be interesting.

That said, let’s get back to the code:

I would’ve liked to replace iterEltFn with something like map, but the SingleNodeIterator won’t coerce to a sequence… Someone want to wrap it for me, I’d take that.

The main function parseIndex, changed to just this:

    (save_xml_doc (iter2addXml nil (.elements t)) "myResults" nil)

The function iter2addXml returns the combined xml document.

(defn iter2addXml [d sni]
  "If the SingleNodeIterator [sni] has more nodes, call_readXml on nextNodes,
    then if doc is nil, add_events to a new_xml_result with the contents,
    else add_events to the existing doc
   otherwise (sni has no more nodes), return the doc as-is."
  (if (.hasMoreNodes sni)
    (let [n (.nextNode sni)
	  nxt (call_readXml n)
	  i 0]
      (if (nil? nxt)
	(recur d sni)
	(let [d2 (if (nil? d)
		   (new_xml_result)
		   d)
	      c (:content nxt)
	      n (add_events d2 c)]
	  ; here we will do size checking things
	  (recur n sni)
	  )))
    ;; else (no more nodes) just return the doc itself, we're done
    d))

(I realize the the code highlighter I’d used gets messed up by WordPress)  The logic is: if there are more nodes, get those, do call_readXml on the results, if the result is nil, recur without it; else add_events to either a new document (via new_xml_result) or the existing doc, then recurse; if there are no more nodes, return the existing doc.

The function call_readXml is, in my opinion, a somewhat elegant “one-liner.”

(defn call_readXml [e]
  (readXmlFile (uri_to_filename (.. e (getFirstChild) (getChildren)
				    (extractAllNodesThatMatch (new HasChildFilter
								   (new StringFilter "S")))
				    (elementAt 0)(extractLink)))))

Granted, most of this is basically a string of Java functions (“e.getFirstChild().getChildren().extractAllNodesThatMatch(...).elementAt(0).extractLink“); then pass the result to uri_to_filename and pass that result to readXmlFile.

  1. ;; read an XML file — more to do here
  2. ;; N.B. “fn” as a param means “filename” not “function”
  3. (defn readXmlFile [fn]
  4.   ;; read the filename
  5.   (let [buffRd (new java.io.BufferedReader (new java.io.FileReader fn))
  6.     lns (slurp buffRd)]
  7.     ;; if the file contents (lns = lines [of the file])
  8.     ;; starts with an HTTP <<status>>, continue
  9.     (if (. lns startsWith “HTTP”)
  10.       ;; split the next line (the hard way), find the whitespace of this (the status) line –
  11.       ;; what remains is the “status,” number and text –
  12.       ;; and the linebreak of the next line
  13.       (let [lnbrk (.indexOf lns "\n")
  14.         dt (.substring lns (+ lnbrk 1))
  15.         idxSp (.indexOf lns " ")
  16.         status (. lns substring (+ 1 idxSp) (- lnbrk 1))
  17.         eol (.indexOf dt "\n")]
  18.         ; (println “HTTP status:” (. lns substring (+ 1 idxSp) (- lnbrk 1 )))
  19.     (if (not (= status “302 Found”))
  20.       ;; call the hdr function…
  21.       (let [[hdr r2] (hdr (struct-map header
  22.                 :status status
  23.                 :date (.substring dt 6 (- eol 1)))
  24.                   (.substring dt (+ eol 1)))]
  25.         (println “header is” hdr)
  26.         ;; then parse the remaining data from the file
  27.         (clojure.xml/parse (new java.io.ByteArrayInputStream (.getBytes r2))))
  28.       ;; if this was a “302 Found” status, we’re dealing with a VIP exchange,
  29.       ;; there’s nothing useful here…
  30.       (println “Location is:” (. dt substring 10 eol))))
  31.     )))

Similar to what was before, but condensed twice: I could do more, like
“(slurp (buffRd (new java.io.BufferedReader…” and more: using duck-streams — I’m just thinking out loud.

And the hdr function has been re-structured for easier reading:

(defn hdr [sm str]
(let [[ln rem] (nlr str)]
(cond
(.startsWith ln "Cache-Control:") (recur (add_val :control (.substring ln 15) sm) rem)
(.startsWith ln "Keep-Alive:") (recur (add_val :keep-alive (.substring ln 12) sm) rem)
(.startsWith ln "Content-Type:") (recur (add_val :type (.substring ln 14) sm) rem)
(.startsWith ln "Connection:") (recur (add_val :connection (.substring ln 12) sm) rem)
(.startsWith ln "Content-Length:") (recur (add_val :length (.substring ln 16) sm) rem)
(.startsWith ln "Content-Language:") (recur (add_val :language (.substring ln 18) sm) rem)
(= 0 (count (.trim ln))) (list sm rem)
true
(println "no match and length wasn't zero (i.e. unknown, non-blank line):"
(count ln) "'" ln "'"))))

This is a new function:

(defn add_events [d events] ; add these trace events (contents) to the xml doc
(type events) "]")
(if (nil? events) d ; if events was nil, just return the doc unchanged
 ;; otherwise
(let [a (attrs d)
      r (reduce (fn [v i] (conj v i)) (or (:content d) []) events)] (assoc d :content r :attrs (assoc a :count (+ (:count a) (count events)))))))

I realize I’m writing far too much code here. Sorry.
I did wind up re-writing the XML emitting code, but that’s for another day.

My Clojure Code before Updates

August 8, 2011

I’m posting the code for my “project” — the technical details of which are terse.

I’m not going to describe a lot, here, but as I go…

(If you’re not a clojure coder, look away)

  1. ;; Fiddlr_To_Xml
  2. ;; my project to convert a Fiddler2 Session Archive (.SAZ) to XML file(s)
  3. ;; for info on Fiddler2 see http://fiddler2.com/fiddler2/
  4. ;; for this to work (the unzipping of the .SAZ, anyway)
  5. ;; I’m depending on 7-Zip (as you can see below)
  6. (require ‘clojure.contrib.shell)  ; not recommended, but to get things working…
  7. (def zipExe \”C:\\Program Files\\7-Zip\\7z.exe\”)  ; constant strings
  8. (def FiddlrCaptsDir “C:\\Documents and Settings\\gparks1\\My Documents\\Fiddler2\\Captures”)
  9. ;; given a filename (a fiddler session arhive), unzip it
  10. (defn unzip [fn]
  11.     (println “unzipping “ fn “…”)
  12.     (let [res (clojure.contrib.shell/sh zipExe "x" fn "-y" :dir FiddlrCaptsDir)]
  13.                     ;(prn res)
  14.       (println “Exit Code: “ (:exit res))
  15.       res))
  16. ; for whatever reason, I’m not getting a real exit code, but it works, so I don’t care

(via http://quickhighlighter.com)

Now, the following felt like real functional programming (because I’m actually treating a function as a first class object):

;; my original function for printing (all) the elements of a SimpleNodeIterator
; (defn printElt [i]
;  (if (. i hasMoreNodes)
;    (let [n (. i nextNode)]
;      (println n)
;      (printElt i))));; my revised helper function — used for many things, including printing
;; given a SimpleNodeIterator, and a function
;; iterate and call fn for each
;;
;; (I bet there’s an easier way, but this works for now)
;; I was just happy to be passing a function around
;; as an argument to another function…
(defn iterEltFn [i fn]
(if (. i hasMoreNodes)
(let [n (. i nextNode)]
(fn n)
(recur i fn))))

And using that here and here:

;; new and improved printElt — call iterEltFn w/ a function (println)

(defn printElt [i]

(iterEltFn i (fn [x] (println x))))

;; 2nd function using iterEltFn; this time, print the element, and the children

(defn printEltChild [i]

(iterEltFn i (fn [x]

(println x)

(printElt (.. x (getChildren) (elements)))

(println)

)))

The following defines a “constant,” two helper functions and a structure (used below):

; ‘global’ filter on tracer string
(def fStr (new org.htmlparser.filters.StringFilter “/prweb/PRTraceServlet?pzDebugRequest=Trace”)); helper function to take a URI and convert to a FileName string
(defn uri_to_filename [uri]
(if (= 0 (.. uri (substring 0 6) (toUpperCase) (compareTo “FILE:/”)))
(let [r (. uri substring 6)]
(.. r (replace “%20″ ” “) (replace “/” \\)))));; helper function to split a string into this line (up to CR), and the remainder…
(defn nlr [s]  ; Next Line and Rest
(let [cr (.indexOf s "\n")
l (.substring s 0 cr)
r (.substring s (+ cr 1))] (list l r)))

;; creating a structure for HTTP header
(defstruct header :status :date :keep-alive :length :control :connection :type :language)

This header function is the first of things to re-do, when I move forward:

;; hdr function
;; parse the lines from a Fiddler2 server response file
;;
;; here is an ‘example’
;;    HTTP/1.1 200 OK
;;    Date: Wed, 06 Jul 2011 18:22:56 GMT
;;    Cache-Control: max-age=0
;;    Keep-Alive: timeout=10, max=980
;;    Connection: Keep-Alive
;;    Content-Type: text/xml;charset=UTF-8
;;    Content-Language: en-US
;;    Content-Length: 76960
;;    
;; (note the ‘blank’ line at the end!)
;; the HTTP (first line, w/ status) and Date have been parsed separately, so the str argument here
;; begins at the 3rd line.  Although there is some semblence of “order” to the lines, it is not dependent on such,
;; save that the blank line be last and the last line is blank.
;; [And there's probably a better way to structure this, as well...]
(defn hdr [sm str]
(let [[ln rem] (nlr str)]
(if (.startsWith ln  “Cache-Control:”)
(let [cc (.substring ln 15)]
(assert (nil? (:control sm)))
(recur (assoc sm :control cc) rem))
(if (.startsWith ln “Keep-Alive:”)
(let [keep (.substring ln 12)]
(assert (nil? (:keep-alive sm)))
(recur (assoc sm :keep-alive keep) rem))
(if (.startsWith ln “Content-Type:”)
(let [typ (.substring ln 14)]
(assert (nil? (:type sm)))
(recur (assoc sm :type typ) rem))
(if (.startsWith ln “Connection:”)
(let [conn (.substring ln 12)]
(assert (nil? (:connection sm)))
(recur (assoc sm :connection conn) rem))
(if (.startsWith ln “Content-Length:”)
(let [len (.substring ln 16)]
(assert (nil? (:length sm)))
(recur (assoc sm :length len) rem))
(if (.startsWith ln “Content-Language:”)
(let [lang (.substring ln 18)]
(assert (nil? (:language sm)))
(recur (assoc sm :language lang) rem))
(if (= 0 (count (.trim ln)))
(list sm rem)
(println “count wasn’t zero:” (count ln)))))))))))

The rest of this entry is presented in reverse order, so the logical flow is evident.

Here is the main function:

;; ****************************************************************************************************
;; * main function to parse an index
;; (no params necessary — the filename is at a well-known location)
;; calls printRowInfo, which does all the heavy lifting
;; (this could actually be re-structured to do the primary filtering here,
;;   and printRowInfo could be simplified…)
;; ****************************************************************************************************
(defn parseIndex []
  (let [parser (new org.htmlparser.Parser (str "file:///" FiddlrCaptsDir "\\_index.htm"))
  ; iter (. parser elements) 
  ; fChStr (new org.htmlparser.filters.HasChildFilter fStr)
fCh2Str (new org.htmlparser.filters.HasChildFilter
(new org.htmlparser.filters.HasChildFilter fStr))
t (. parser parse fCh2Str)  ; a nodelist!
]
; (printElt iter); (printEltChild (. t elements))
    (printRowInfo (. t elements))

  )
)

Which calls printRowInfo for the entire list of elements:
(comments “extracted” from quickhighlighting for “clarity”

;; ****************************************************************************************************
;; main (internal) function
;; given a SingleNodeIterator argument, iterate
;; the function passed to iterEltFn gets the text (already known to contain "pzDebugRequest=Trace"),
;; and the server filename (from the children of the first child) --
;; the link from the first element of the list, which is the anchor containing the text "S" of the children of the 1st TD elt
;; [sSvrFile from datas, which is getFirstChild [TD] getChildren extractAllNodesThatMatch
;;     {[anchors] with children which are strings equal to "S"} elementAt 0, extractLink
;; each iteration calls readXmlFile
;; ****************************************************************************************************

Of interest, see that this function relies exclusively on iterEltFn and only creates an (anonymous) function to pass to it — I could make this a defined function, separating that work out

(defn printRowInfo [r]
(iterEltFn r (fn [n]
(let [datas (. (. n getFirstChild) getChildren) ; slow way to call a member of a result
; the children of the first child (a "TableColumn" [TD]) are:
; LinkTag [A] w/ text ‘C’ (Client)
; LinkTag [A] w/ text ‘S’ (Server)
; LinkTag [A] w/ text ‘M’ (M___)
fChStr (new org.htmlparser.filters.HasChildFilter fStr)
; the following is a short-cut
; n.getChildren().extractAllNodesThatMatch(fChStr).elementAt(0).getFirstChild()…
txt (.. n (getChildren) (extractAllNodesThatMatch fChStr) (elementAt 0)
(getFirstChild))
sSvrFile (.. datas (extractAllNodesThatMatch
(new org.htmlparser.filters.HasChildFilter
(new org.htmlparser.filters.StringFilter “S”)))
(elementAt 0)(extractLink))
]
; (println “Node =” n)
; (println “first child (data) w/ three <A> tags:” datas)
; (println “text node is” txt)
; (println “file is ” (uri_to_filename sSvrFile))
(readXmlFile (uri_to_filename sSvrFile))))))

And, finally, a good, stand-alone function to read an XML file:

;; read an XML file — more to do here
;; N.B. “fn” as a param means “filename” not “function”
(defn readXmlFile [fn]
;; read the filename
(let [buffRd (new java.io.BufferedReader (new java.io.FileReader fn))
lns (slurp buffRd)
h (first lns)]
;; if the file contents (lns = lines [of the file])
;; starts with an HTTP <<status>>, continue
(if (. lns startsWith “HTTP”)
;; split the next line (the hard way), find the whitespace of this (the status) line –
;; what remains is the “status,” number and text –
;; and the linebreak of the next line
(let [lnbrk (.indexOf lns "\n")
dt (.substring lns (+ lnbrk 1))
idxSp (.indexOf lns " ")
status (. lns substring (+ 1 idxSp) (- lnbrk 1))
eol (.indexOf dt "\n")
]
; (println “HTTP status:” (. lns substring (+ 1 idxSp) (- lnbrk 1 )))
(if (not (= status “302 Found”))
;; call the hdr function…
(let [[hdr r2] (hdr (struct-map header :status status :date (.substring dt 6 eol))
(.substring dt (+ eol 1)))]
(println “header is” hdr)
;; then parse the remaining data from the file
(let [p (clojure.xml/parse (new java.io.ByteArrayInputStream (.getBytes r2)))]
;; ** more to do here **
p)
)
;; if this was a “302 Found” status, we’re dealing with a VIP exchange, and there’s nothing useful here…
(println “Location is:” (. dt substring 10 eol))
))
)))

Commentary, what there is, to follow…

What happened at my latest assignment

June 12, 2011

I’ll get back to tech. writing soon, but first my last thoughts about my ‘old’ job:

I’m writing separately about my experience with IBM in general, and here specifically about my last assignment.
Before I even showed up at my new assignment, I told them that I had already scheduled a week of vacation (first week in March) — plane tickets bought and everything, and I made no secret of that. I arrived to find that I did not have any access — either to the building itself, nor to the computer network; it took over two weeks to get everything installed and access granted (less time for the physical access, but coming to my desk did not permit me to get any work done, only attend meetings). While I was on vacation, I got an e-mail that requirements were due for my new assignment that day — not only did I get the e-mail while I was known to be out of the office, the e-mail was sent on the due date, not before.

Well, I worked as I could, learning new technology, as well as a new environment — new people, new processes (or lack thereof), new red tape…

Here is the bragging section: I started with minimal knowledge of some of these, and by the end, I had written a new method for a web service, using a framework I was unfamiliar with, communicating with the database via Hibernate (another first, for me), and also modified the client application to call the web service, and changed the UI, using JGoodies (new — though how much use of that is open for debate) — I worked on almost every layer of this n-tier architecture, Java application, using Java version 1.5 on the client, and only 1.4 on the server, running on Websphere 6.1 (new), coding in RAD 7.0.1. And the entire infrastructure was a mystery to me — I’ve never seen a diagram, yet. All the while, I navigated, a new social and political landscape — sometimes more successfully, sometimes less.

When it came to the Thursday before the due date, I had made clear that things weren’t all right, and finally let it be known clearly that the project wouldn’t be done this week, even to the point of taking the (personal) initiative to call the project manager’s boss on the phone and say, “this isn’t feasible.” So I was asked, “well, could you work the weekend.” (Note there is no question mark on that sentence — it wasn’t really a question.) Not knowing any of the players, and without any offered assistance, without communication with the people who needed to approve, that would be difficult. Arrangements were made to make two people available, and I was led to believe a third, who had key knowledge, but when I called him, repeatedly, on Saturday, he told me in no uncertain terms that he couldn’t help. By five o’clock Saturday, I’d accomplished more than I thought possible, but still called the final contact and said, “here’s where things are, and here is where they will stay — I won’t work on Sunday, and we’ll finish next week.”

It still took until Tuesday, but it was done. Then some support was given while the project was promoted, and for the next month after that, I sat and did nothing, because there was no work to do… So explain to me again why it was so important to do that last job in a rush??

That’s the most I’m going to complain, except for this — last week, I’m told there was an “all-hands” meeting (to which I wasn’t invited, and it wasn’t just me — there was at least one other person); one of the areas discussed was communication, but the meeting wasn’t communicated to everyone. [You can put your own emoticon, or exclamation here.]

Contrast that with starting my new job: I got two e-mail msgs the week before letting me know where to go and who would have my new laptop, and my initial account set-up. There was still a snafu along the way — isn’t there always? But requests for access were granted in short order and I was contributing and productive by the 3rd day.

Good people along the way

May 18, 2011

There have been many people who helped, who deserve special mention, and a couple of companies to reference, which I’d be happy to do business with again.  A surprise assist is due toYan Tsirklin, and I owe him thanks — be sure to check out SocialWish, and, if you need “unique and exclusive written articles created to your specifications”, check out Text Broker.
I’d gladly work for Steve Mueller anytime, anywhere.  Also Steve Musch is an excellent technical recruiter, who I was pleased to work with, although that opportunity did not work out (through no fault of his).  Ethan Scheetz, at Recruiters of Minnesota helped me, and almost landed me one job (I came in 2nd, due to myself, not Ethan!).

I want to thank my wife, for her patience, understanding, help and support, and un-ending love.

Intertech is another good company; I am happy to know people there, and maybe someday work with…  Steve Z. at TekSystems is a great guy that both my brother and I know, and I’d gladly recommend.  Another person known to both is Heidi Cline, recruiter at Concord USA – she was extremely kind and helpful.  I can’t say enough good about her.

Also, lest anyone think I only had “bad” experiences at IBM, there is one person I consider a mentor, who aided me both while there, and while leaving — thank you, Scott.

All in all, I consider myself blessed by the people I know, and who know me.

How I got here

May 18, 2011

In two weeks time, I will no longer be employed by IBM — I have been laid off, part of a “large” RIF (“reduction in force” — also a verb, as in “I’ve been RIF’d.”)  Here’s what happened:

Some of this I’ve already written, but here is more detail.  I’m posting this now because I’ve made a decision.

The story really starts back when when I joined IBM.  Before I was hired, there was a friend who suggested it was not the best place to work — they had their own story of administrative nightmare.  Well, it took the company almost six weeks* to decide if they wanted to hire me or not.  Three “managers” at BlueCross BlueShield of Minnesota suggested, strongly, that I apply, and they would put a good word in for me — for which I thank them greatly.  I interviewed, and was then told, by a different person in HR that I wasn’t accepted, but no details were given, although I asked.  So I contacted the HR rep. that I initially spoke to, who seemed mystified that there was no info, but also quite certain that I should’ve known the new person was now the point of contact; however, he agreed to look into it and give me some idea of what wasn’t right — and, to his credit, he did call me back and say, “wait a minute, we’re reconsidering….”  So although I continued to search, I did wait, and eventually, quite late, found out that I would be hired by IBM.

* I am not sure of the length of time.  I do know another person who was in a similar, but worse, situation.

It was quite interesting to leave BCBS MN, take two weeks off, go to training in Washington, D.C., and return to the very same desk, now working for a different company, doing the same thing I was…

Fast forward to February this year — that first week, I’d had a conversation with my manager to discuss my next assignment, and by Wednesday he’d e-mailed the previous project manager to find out when I’d be finished.  The response on Thursday was “Greg was done working on this project as of Tuesday.”  Consequently, I spent the next week both finishing up and sending documentation and notices to people, and also trying to secure a new assignment; contact for the next client, however, was initiated by an IBM manager there, and I reported to my new work site the next week.

Then, I was told at the end of the month, after only two weeks at the new assignment, that I was being laid off.  And I was given a month notice, the week before I was on PTO for a week.

This post is not intended as sour grapes!  I’m not overly complaining about my state, just reporting the interesting and sometimes humorous developments along the way.

What else happened at that client is another story… [this link doesn't work, yet]

I returned from vacation on a Friday, and updated my status on LinkedIn, and sent a couple of e-mails.  By Tuesday the next week, I had three opportunities — one of which didn’t pan out because the process was too far along (another candidate was offered the position before the end of the week), one because the HR person did not want to deal with a (potential) “do not compete” clause (there was none to speak of, but that is his loss).  One of which eventually led to an offer, which I’ll accept and start presently.

That started at the end of February, and I was told my last day would be March 28th.  I asked if I could appeal, and was told, “yes, but it probably won’t work.”  Others told me similarly, that IBM was less and less approving appeals.  But in spite of many people saying so, I did get a two month extension.  Now, my last day is May 31st.
The point is, just like when I started, as well as at the end, communication within a huge company is rife with mistakes.  To the point that, for three months, administratively, one branch of IBM did not know where I was, while I was (successfully, appropriately) billing my time to my latest client — the resource/assignment e-mails kept suggesting I look for a new assignment, because I was, or would be finished with the current one very soon (!!).

In all this, there are some people I want to recognize, and thank.


Follow

Get every new post delivered to your Inbox.

Join 1,086 other followers