You are currently browsing the tag archive for the ‘programming’ tag.
I compiled some programming language popularity statistics in April 2009, October 2009, October 2010, September 2011 and August 2012 . Here’s an update for February 2013:
I made a number of Google searches of the forms below and summed the results:
"implemented in <language>" "written in <language>"
Naturally this is of very limited utility, and the numbers are only useful when comparing relatively within the same search since the number of results Google returns can vary greatly over time.
I’ve divided the table into sections based on large percentage drops from one language to the next.
|------+-----------------+------------+----------+-------+------------| | Rank | Language | # Search| Previous | Rank | Delta from | | | | Results| Rank | Delta | Apr '09 | |------+-----------------+------------+----------+-------+------------| | 1 | PHP | 52,699,000| 1 | | 3 | | 2 | C | 39,330,000| 2 | | -1 | | 3 | C++ | 26,490,000| 4 | 1 | | | 4 | Python | 22,410,000| 3 | -1 | 1 | | 5 | C# | 21,474,000| 5 | | 2 | |------+-----------------+------------+----------+-------+------------| | 6 | Perl | 11,013,000| 8 | 2 | | | 7 | Java | 10,150,000| 6 | -1 | -5 | | 8 | JavaScript | 7,340,000| 9 | 1 | 1 | |------+-----------------+------------+----------+-------+------------| | 9 | Ruby | 3,456,000| 7 | -2 | 1 | | 10 | Lisp Family (1) | 2,955,000| 10 | | -2 | | 11 | FORTRAN | 2,256,000| 11 | | N/A | | 12 | Lisp | 1,708,000| 17 | 5 | | | 13 | R | 1,305,000| 21 | 8 | N/A | | 14 | Tcl | 1,072,100| 13 | -1 | -1 | | 15 | Lua | 1,011,000| 19 | 4 | 5 | | 16 | ML Family (2) | 988,400| 16 | | -2 | | 17 | Erlang | 842,000| 18 | 1 | -1 | | 18 | COBOL | 729,200| 23 | 5 | N/A | | 19 | Haskell | 707,000| 12 | -7 | -4 | | 20 | Common Lisp | 557,000| 20 | | -2 | | 21 | OCaml | 528,000| 24 | 3 | -4 | | 22 | Prolog | 521,000| 25 | 3 | -3 | | 23 | (S)ML (3) | 496,800| 27 | 4 | 1 | | 24 | Scala | 426,100| 22 | -2 | 1 | | 25 | Scheme | 347,000| 28 | 3 | -14 | | 26 | Groovy | 320,000| 14 | -12 | N/A | |------+-----------------+------------+----------+-------+------------| | 27 | Smalltalk | 201,400| 29 | 2 | -6 | | 28 | Go | 201,200| 15 | -13 | N/A | | 29 | CoffeeScript | 182,800| 31 | 2 | N/A | | 30 | Clojure | 173,100| 30 | | -2 | | 31 | Forth | 128,800| 26 | -5 | -8 | | 32 | Caml | 102,600| 34 | 2 | -6 | | 33 | Racket | 93,500| 33 | | N/A | | 34 | Arc | 76,400| 32 | -2 | -12 | | 35 | Io | 60,200| 35 | | -8 | |------+-----------------+------------+----------+-------+------------|
(1) combines Lisp, Scheme, Common Lisp, Racket, Arc & Clojure
(2) combines OCaml, (S)ML, Caml
(3) summed separate searches for standard ml, sml & ml
The Problem
I noticed some unusual behavior with respect to YAML String serialization between my Linux production system and my Mac OSX development system.
After dumping the production database via pg_dump -O –no-acl mydb | gzip > ~/mydb.sql.gz and then restoring it on my development system via rake db:drop; rake db:create; psql mydb < mydb.sql, I noticed that a particular serialized field in my Rails app that should always be an Array of String objects occasionally contained Integers.
After a little research and experimentation, I discovered that the production Linux system would occasionally omit quotations around Strings containing only numeric digits. I haven’t analyzed the pattern fully, but here are some examples where the YAML serialization did or did not use quotes:
- “90103″
- 000080
- “000071″
- “000124″
- “000003″
- 008397
- 000408
- 000009
- 000188
- “000021″
Further investigation revealed that the Linux production system was using Syck (a “dated C implementation of YAML 1.0″) and my Mac OSX development system was using psych (a “libyaml wrapper (in Ruby core for 1.9.2)”). libyaml is a “fast C implementation of YAML 1.1. So, either the quotation rules have changed between YAML 1.0 and YAML 1.1, or there is a bug in one of the implementations (likely Syck).
The Solution
The solution for proper “future” behavior is pretty simple. Install libyaml on the Linux system as follows:
wget http://pyyaml.org/download/libyaml/yaml-0.1.4.tar.gz tar xzf yaml-0.1.4.tar.gz cd yaml-0.1.4 ./configure make make install
I think that’s enough, but I went ahead and rebuilt my Ruby 1.9.2 just in case it needed to know about the existence of libyaml at build time.
The solution for converting my database with YAML 1.0 serialization to YAML 1.1 serialization is a bit trickier. Since the “dump” and “load” operations are matched for a particular version of YAML, it seems difficult to load the data using YAML 1.0 (thereby retaining the String type when reading an unquoted 000088) and then dump the data using YAML 1.1 (to get proper quoting of ’000088′). Further complicating this is the fact that Rails handles the serialization operations automatically.
It does appear possible to dynamically switch between syck and psyck by using the following:
YAML::ENGINE.yamler = 'syck' YAML::ENGINE.yamler = 'psych'
So, one option is to repeatedly switch to syck, read in data, switch to psych, and then write the data. <sigh>
Update:
It appears that due to the semantics of the Rails serialize function, it’s not enough to just read the model object using syck and then immediately write with psych because that doesn’t appear to be enough to cause the field to be deserialized. I had to refer to the field for each object. This is a pain because it prevents me from doing a generic loop where I can handle all model objects easily w/o reference to their specific fields.
I’ll withhold judgment for a while, but my first inclination is to consider abandoning YAML serialization for something a little more robust and portable.
Update 2:
It appears my welcome from psych is a serious memory leak. I’ve been running long running Ruby/Rails processes for years, and this is the first time I’ve experienced a failure due to an out of memory condition. There are a number of Google hits regarding the issue. After I fix the leak, I’ll begin researching alternatives to YAML serialization in Rails.
Update 3:
The number of bug reports on psych and rubygems I’ve had to wade through recently is amazing. My current solution is to remove the psych system gem and install Ruby 1.9.3p0 which required upgrading Passenger to the latest version from source to get Ruby 1.9.3 compatibility. I still had to track down a few odd errors such as “undefined method `yaml’ for #<Psych::Nodes::Stream:…>” and “invalid date format in specification: “2011-10-02 00:00:00.000000000Z”" – all because I chose to use the default Rails serialization assuming there would be no issues. Lesson learned.
See Part Five
I compiled some programming language popularity statistics in April 2009, October 2009 and October 2010 . Here’s an update for September 2011:
I made a number of Google searches of the forms below and summed the results (previous posts averaged the results):
"implemented in <language>" "written in <language>"
Naturally this is of very limited utility, and the numbers are only useful when comparing relatively within the same search since the number of results Google returns can vary greatly over time.
| Language | Total | Prev. Position | Position Delta |
|---|---|---|---|
| C | 10,360,000 | 2 | 1 |
| PHP | 10,351,000 | 1 | -1 |
| C++ | 6,495,000 | 3 | 0 |
| Python | 5,759,000 | 5 | 1 |
| C# | 5,335,000 | 4 | -1 |
| Java | 4,890,000 | 8 | 2 |
| Perl | 3,702,000 | 6 | -1 |
| JavaScript | 3,077,000 | 7 | -1 |
| Ruby | 1,654,000 | 9 | 0 |
| Lisp Family1 | 1,022,870 | 11 | 1 |
| FORTRAN | 975,600 | 10 | -1 |
| Tcl | 594,500 | 12 | 0 |
| Lisp | 486,000 | 14 | 1 |
| Haskell | 450,500 | 16 | 2 |
| Erlang | 419,700 | 13 | -2 |
| Lua | 367,100 | 18 | 2 |
| ML Family2 | 348,400 | 17 | 0 |
| COBOL | 308,270 | 15 | -3 |
| Common Lisp | 254,900 | 19 | 0 |
| OCaml | 240,300 | 21 | 1 |
| Prolog | 224,000 | 20 | -1 |
| Scala | 203,400 | 23 | 1 |
| Scheme | 184,700 | 22 | -1 |
| Smalltalk | 129,700 | 24 | 0 |
| Clojure | 84,600 | 27 | 2 |
| (S)ML3 | 83,630 | 25 | -1 |
| Forth | 69,980 | 26 | -1 |
| Caml | 24,470 | 28 | 0 |
| Io | 17,700 | 30 | 1 |
| Arc | 12,670 | 29 | -1 |
1 combines Lisp, Scheme, Common Lisp, Arc & Clojure
2 combines OCaml, (S)ML, Caml
3 summed separate searches for sml and ml
See Part Five
I compiled some programming language popularity statistics in April 2009 and October 2009 . Here’s an update for October 2010:
I made a number of Google searches of the forms below and averaged the results:
"implemented in <language>" "written in <language>"
Naturally this is of very limited utility, and the numbers are only useful when comparing relatively within one column since the number of results Google returns can vary greatly over time.
| Language | Apr 2009 | Oct 2009 | Oct 2010 | Position Delta |
|---|---|---|---|---|
| PHP | 680,000 | 5,083,500 | 14,096,000 | +3 |
| C | 1,905,500 | 16,975,000 | 9,675,000 | -1 |
| C++ | 699,000 | 6,270,000 | 6,510,000 | -1 |
| C# | 349,700 | 2,125,000 | 5,132,000 | +4 |
| Python | 396,000 | 3,407,000 | 5,114,500 | +1 |
| Perl | 365,500 | 3,132,500 | 4,675,000 | +1 |
| JavaScript | 102,700 | 1,163,000 | 2,120,000 | +4 |
| Java | 850,000 | 5,118,000 | 1,495,500 | -5 |
| Ruby | 99,650 | 227,000 | 1,426,000 | +13 |
| FORTRAN | 1,621,000 | 770,850 | 0 | |
| Lisp Family1 | 176,507 | 3,489,650 | 399,685 | -6 |
| Tcl | 44,800 | 382,000 | 313,400 | +5 |
| Erlang | 22,285 | 161,700 | 188,800 | +12 |
| Lisp | 61,900 | 486,500 | 174,050 | +1 |
| COBOL | 247,300 | 166,435 | +6 | |
| Haskell | 22,550 | 280,500 | 157,150 | +4 |
| ML Family2 | 29,062 | 1,003,800 | 149,005 | -5 |
| Lua | 13,065 | 131,800 | 128,150 | +9 |
| Common Lisp | 20,600 | 554,500 | 112,750 | -5 |
| Prolog | 17,750 | 390,500 | 100,000 | -4 |
| OCaml | 22,000 | 343,500 | 99,050 | -3 |
| Scheme | 86,450 | 2,100,000 | 82,650 | -13 |
| Scala | 3,570 | 66,250 | 65,950 | +6 |
| Smalltalk | 9,105 | 187,500 | 56,950 | 0 |
| (S)ML3 | 5,173 | 590,700 | 42,130 | -12 |
| Forth | 6,465 | 146,450 | 25,880 | 0 |
| Clojure | 782 | 62,200 | 23,525 | +3 |
| Caml | 1,889 | 69,600 | 7,825 | 0 |
| Arc | 6,775 | 286,500 | 6,710 | -10 |
| Io | 1,760 | 198,500 | 3,025 | -7 |
1 combines Lisp, Scheme, Common Lisp, Arc & Clojure
2 combines OCaml, (S)ML, Caml
3 summed separate searches for sml and ml
Chapter 6 – Case Analysis
Tuple types are homogeneous e.g. all values of type int*real are pairs containing an int and a real. Match failures occur at compile time. Types that have more than one form, such as int, are heterogeneous. Pattern matches fail at run time as a bind failure.
ML defines functions over heterogeneous types using clausal function expressions. For example:
fn pat1 => exp1 | pat2 => exp2 | ... | patn => expn
Each component pat => exp is called a clause, or a rule. The entire assembly of rules is a called a match. For example:
val recip : int -> int =
fn 0 => 0 | n:int => 1 div n
The fun notation is generalized so we can be more concise:
fun recip 0 = 0 | recip (n:int) = 1 div n
Case analysis on the values of a heterogeneous type is performed by application of a clausally-defined function. The notation:
case exp of pat1 => exp1 | ... | patn => expn
is short for the application:
(fn pat1 => exp1 | ... | patn => expn) exp
Matches are subject to two forms of “sanity check” – exhaustiveness checking and redundancy checking.
Chapter 7 – Recursive Functions
For a function to be able to call itself, it needs a name. For example:
val rec factorial : int->int =
fn 0 => 1 | n:int => n * factorial (n-1)
or using fun notation:
fun factorial 0 = 1 | factorial (n:int) = n * factorial (n-1)
Iteration
If we define a helper function that accepts an accumulator we can reduce the storage needed:
fun helper (0,r:int) = r | helper (n:int,r:int) = helper (n-1,n*r) fun factorial (n:int) = helper (n,1)
It’s better programming style to hide the helper function w/in a local declaration:
local
fun helper (0,r:int) = r
| helper (n:int,r:int) = helper (n-1,n*r)
in
fun factorial (n:int) = helper (n,1)
end
Tail recursive functions are analogous to loops in imperative languages – they iterate the computation w/o needing auxiliary storage.
Mutual Recursion
Definitions which are mutually recursive can be joined together with the and keyword to indicate they are defined simultaneously by mutual recursion:
fun even 0 = true | even n = odd (n-1) and odd 0 = false | odd n = even (n-1)
Chapter 8 – Type Inference and Polymorphism
Standard ML allows you to omit type information whenever it can be determined from context. Consider the following:
fn s:string => s ^ "n"
There is no need to declare the type of s since it can be inferred from the context, so we may write:
fn s => s ^ "n"
A type scheme is a type expression involving one or more type variables standing for an unknown, but arbitrary type expression. Type variables are written ‘a (pronounced alpha), ‘b (pronounced beta), etc. For example, the type scheme ‘a->’a has instances int->int, string->string, (int*int)->(int*int), and (‘b->’b)->(‘b->’b), etc. It does not have the type int->string.
We may bind an identity function to the variable I as follows:
val I : 'a->'a = fn x=>x
We may also write:
fun I(x:'a) : 'a = x
Standard ML eliminates the need to ascribe a type scheme to the variable:
val I = fn x=>x or fun I(x) = x
Chapter 9 – Programming with Lists
The values of type type list are the finite lists of values of type type:
1. nil is a value of type typ list. 2. if h is a value of type typ, and t is a value of type typ list, then h::t is a value of type typ list. 3. Nothing else is a value of type typ list.
The type expression typ list is a postfix notation for the application of the type constructor list to the type typ.
A value val of type typ list has the form:
val1 :: (val2 :: (… :: (valn :: nil) … ))
The :: operator is right-associative, so we may omit parentheses:
val1 :: val2 :: … :: valn :: nil
Or, we may use list notation:
[ val1, val2, ..., valn ]
Computing With Lists
Some examples:
fun length nil = 0 | length (_::t) = 1 + length t
Note we do not give a name to the head of the list, instead we use a wildcard _
fun append (nil, l) = l | append (h::t, l) = h :: append (t, l)
The latter is built into Standard ML and is written using infix as: exp1 @ exp2
fun rev nil = nil | rev (h::t) = rev t @ [h]
The running time of the latter is O(n2). The following definition makes use of an accumulator and has a running time of O(n):
local
fun helper (nil, a) = a
| helper (h::t, a) = helper (t, h::a)
in
fun rev' l = helper (l, nil)
end
Chapter 10 – Concrete Data Types
Non-Recursive Datatypes
Example of nullary i.e. zero argument, constructors:
datatype suit = Spades | Hearts | Diamonds | Clubs
It is conventional to capitalize the names of value constructors, but this is not required by the language.
Datatypes may be parameterized by a type:
datatype 'a option = NONE | SOME of 'a
The values are NONE or Some val, where val is a value of type typ. The option type constructor is pre-defined in Standard ML.
Option types can also be used in aggregate data structures:
type entry = { name:string, spouse string option }
An entry for an unmarried person would have a spouse field with a value of NONE.
Recursive Datatypes
datatype 'a tree = Empty | Node of 'a tree * 'a * 'a tree
1. The empty tree Empty is a binary tree.
2. If tree_1 and tree_2 are binary trees, and val is a value of type type, then Node (tree_1, val, tree_2) is a binary tree.
3. Nothing else is a binary tree.
A function to compute the height of a binary tree, and one to compute the number of nodes:
fun height Empty = 0 | height (Node (lft, _, rht)) = 1 + max (height lft, height rht) fun size Empty = 0 | size (Node (lft, _, rht)) = 1 + size lft + size rht
Heterogeneous Data Structures
The tree data type above requires that the type of the data items at the nodes must be the same for every node of the tree. To represent a heterogeneous tree, the data item must be labelled with enough info to determine the type at run-time.
datatype int_or_string = Int of int | String of string type int_or_string = int_or_string tree
Datatype declarations and pattern matching can be useful for manipulating the abstract syntax of a language. Consider an example representing arithmetic expressions:
datatype expr =
Numeral of int |
Plus of expr * expr |
Times of expr * expr
fun eval (Numeral n) = Numeral n
| eval (Plus (e1, e2)) =
let
val Numeral n1 = eval e1
val Numeral n2 = eval e2
in
Numeral (n1+n2)
end
| eval (Times (e1, e2)) =
let
val Numeral n1 = eval e1
val Numeral n2 = eval e2
in
Numeral (n1*n2)
end
If we extend the expr datatype as follows:
datatype expr = Numeral of int | Plus of expr * expr | Times of expr * expr Recip of expr
The compiler will complain about eval being incompatible with the new version of expr. Recompiling eval will produce an inexhaustive match warning since eval lacks a case for Recip. This is one of the benefits of static typing provided in Standard ML.
I found a handy article for sending growl notifications from Emacs and made some modifications for my particular setup.
Step One: Register Emacs with Growl
We need to register Carbon Emacs with Growl. This is a one time setup task. The following AppleScript script can be run in Script Editor.
tell application "GrowlHelperApp"
-- Declare a list of notification types
set the allNotificationsList to {"Emacs Notification"}
-- Declare list of active notifications. If some of them
-- isn't activated, user can do this later via preferences
set the enabledNotificationsList to {"Emacs Notification"}
-- Register our application in Growl.
register as application "Emacs.app" all notifications allNotificationsList default notifications enabledNotificationsList icon of application "Emacs.app"
end tell
Step Two: Create Growl Notification Script
This is a template of the AppleScript that will be used in the Emacs Lisp function. It can be tested by executing it in the Script Editor.
tell application "GrowlHelperApp" notify with name "Emacs Notification" title "Emacs alert" description "Message!!!" application name "Emacs.app" end tell
Step Three: Emacs Lisp Function
Create an Emacs Lisp function that can be invoked with a title and message.
(defun bja-growl-notification (title message &optional sticky)
"Send a Growl notification"
(do-applescript
(format "tell application "GrowlHelperApp"
notify with name "Emacs Notification" title "%s" description "%s" application name "Emacs.app" sticky %s
end tell"
title
(replace-regexp-in-string """ "''" message)
(if sticky "yes" "no"))))
This can be tested with the following function which sends two growl notifications. The “sticky” one requires an explicit dismissal.
(defun bja-growl-test () (interactive) (bja-growl-notification "Emacs Notification" "This is my message") (bja-growl-notification "Emacs Notification" "This is my sticky message" t))
UPDATE: I noticed bja-growl-notification was failing if the message had embedded double quotes, so I added a call to replace-regexp-in-string to replace them with two apostrophes.
UPDATE 2: My main motivation for setting this up was to be able to generate growl notifications from ERC (Emacs IRC client). However, I quickly realized that having a generic reminder that I can easily fire up from within Emacs is very handy. Here’s the code:
(defun bja-growl-timer (minutes message)
"Issue a Growl notification after specified minutes"
(interactive (list (read-from-minibuffer "Minutes: " "10")
(read-from-minibuffer "Message: " "Reminder") ))
(run-at-time (* (string-to-number minutes) 60)
nil
(lambda (minutes, message)
(bja-growl-notification "Emacs Reminder" message t))
minutes
message))
I first became interested in functional programming when I was exposed to Python, Ruby & JavaScript a number of years ago. Since then I’ve looked into Arc, Clojure, Common Lisp, Haskell, Logo, ML & Scheme. I haven’t yet determined whether I’ll be more productive in any of them than I am with Ruby for developing web applications, but I do find them quite interesting.
After bumping into a number of local programmers who expressed an interest in functional programming, I thought it might be a good time to start a local group that focused on functional programming languages, so I did a couple days ago.
TriFunc.org is a group for programmers who are interested in functional programming languages and live near the Research Triangle area of North Carolina.
If you live in the area and have an interest in functional programming languages, feel free to dive in and start participating in the Google Group discussions. Once we reach a critical mass, I expect we’ll produce a meeting schedule, etc., but that will depend on where the group wants to take this.
I recently obtained a mobile broadband device that has a built in GPS receiver and can emit NMEA sentences. My old Garmin portable GPS can emit NMEA also, but it’s a pain to hookup to the laptop. Combining a GPS unit in a mobile broadband device is a great idea.
Update: it appears that the accuracy radius of the wireless card is quite a bit larger than my old Garmin unit. The Garmin is usually between 15 and 30 feet, but the Sierra Wireless 598U ranges from 100 to 1,000 feet or more.
After installing the ruby-serialport gem, I was able to write a simple Ruby program to read GPS information from the device and update a remote file on my web server to allow real time location tracking.
Add a simple server side script to read the file and update an iframed Google Map and you’re all set.
The code is also in the Ruby section of my sample code repository on Github.
sudo gem install ruby-serialport
#!/usr/local/bin/ruby
# Author: Brian Adkins
# Date: 2009/04/08
# Copyright 2009 Brian Adkins - All Rights Reserved
#
# Ruby program to retrieve and parse GPS information (via NMEA sentences)
# from a Sprint Sierra Wireless 598U device.
#
# ruby gps-nmea.rb # prints latititude/longitude info
# ruby gps-nmea.rb update-remote # scp a file of location info to a remote server
#
# This program depends on the ruby-serialport gem:
# sudo gem install ruby-serialport
#
# From: http://www.gpsinformation.org/dale/nmea.htm#GGA
# $GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47
# Where:
# GGA Global Positioning System Fix Data
# 123519 Fix taken at 12:35:19 UTC
# 4807.038,N Latitude 48 deg 07.038' N
# 01131.000,E Longitude 11 deg 31.000' E
# 1 Fix quality: 0 = invalid
# 1 = GPS fix (SPS)
# 2 = DGPS fix
# 3 = PPS fix
# 4 = Real Time Kinematic
# 5 = Float RTK
# 6 = estimated (dead reckoning) (2.3 feature)
# 7 = Manual input mode
# 8 = Simulation mode
# 08 Number of satellites being tracked
# 0.9 Horizontal dilution of position
# 545.4,M Altitude, Meters, above mean sea level
# 46.9,M Height of geoid (mean sea level) above WGS84
# ellipsoid
# (empty field) time in seconds since last DGPS update
# (empty field) DGPS station ID number
# *47 the checksum data, always begins with *
require 'rubygems'
require 'serialport'
# Emacs macro to reset user modified values (highlight, then: M-x eval-region )
# ((lambda (&optional arg) "Keyboard macro." (interactive "p") (kmacro-exec-ring-item (quote ("USERNAME
"
372"HOSTNAME
"
372"REMOTE_DIR
"
372"" 0 "%d")) arg)))
# --- MODIFY THESE -- #
USERNAME = "" # Username for remote host
HOSTNAME = "" # Remote host name e.g. foo.com
REMOTE_DIR = "" # Remote directory e.g. /var/www/bar
# --- MODIFY THESE -- #
port_str = '/dev/cu.sierra05'
baud_rate = 9600
data_bits = 8
stop_bits = 1
parity = SerialPort::NONE
sp = SerialPort.new(port_str, baud_rate, data_bits, stop_bits, parity)
# lat is of the form 4807.038 where the first 2 digits are degrees and
# the remainder is minutes.
# dir is either 'N' or 'S'
def convert_lat lat, dir
degrees = lat[0,2].to_f + (lat[2,lat.length-2].to_f / 60.0)
dir == 'N' ? degrees : -degrees
end
# lon is of the form 01131.000 where the first 3 digits are degrees and
# the remainder is minutes.
# dir is either 'E', or 'W'
def convert_lon lon, dir
degrees = lon[0,3].to_f + (lon[3,lon.length-2].to_f / 60.0)
dir == 'E' ? degrees : -degrees
end
TEMP_PATH = '/tmp'
TEMP_FILE = 'location.txt'
def update_remote_info lat, lon
File.open("#{TEMP_PATH}/#{TEMP_FILE}", 'w') do |tf|
tf.puts Time.now.to_s
tf.puts "#{lat},#{lon}"
end
puts 'Updating remote location info'
`scp #{TEMP_PATH}/#{TEMP_FILE} #{USERNAME}@#{HOSTNAME}:#{REMOTE_DIR}/#{TEMP_FILE}`
File.delete("#{TEMP_PATH}/#{TEMP_FILE}")
end
# 99 requests should be sufficient to find a $GPGGA sentence
99.times do
if (str = sp.gets) =~ /^$GPGGA/
fix = str.split(',')
if fix[6] == '1'
lat = convert_lat(fix[2], fix[3])
lon = convert_lon(fix[4], fix[5])
if ARGV[0] == 'update-remote'
update_remote_info(lat,lon)
elsif
puts "#{lat}, #{lon}"
end
exit 0
end
end
end
puts "Invalid data - GPS coordinates not found"
Sunrise, Sunset & Twilight
I was curious about the exact time of sunrise & sunset at my location, so I found this US Naval Observatory site. In the process, I learned a more precise definition of twilight. I wanted to be able to automate the process of retrieving the information, so my first attempt was to simply put the query parameters used in the form in the URL as an HTTP GET request, but the server wouldn’t accept that, so I needed to issue an HTTP POST request.
Ruby Code
Ruby is a great language for this sort of task, so I put together the following simple program:
require 'net/http'
YOUR_ID = '' # A unique ID per comment above
YOUR_CITY = '' # The name of your city
YOUR_STATE = '' # Two letter state abbreviation
now = Time.now
month = now.month
day = now.day + 1 # Tomorrow
year = now.year
Net::HTTP.start('aa.usno.navy.mil') do |query|
response = query.post('/cgi-bin/aa_pap.pl',
"FFX=1&amp;amp;ID=#{YOUR_ID}&amp;amp;xxy=#{year}&amp;amp;xxm=#{month}&amp;amp;xxd=#{day}&amp;amp;st=#{YOUR_STATE}&amp;amp;place=#{YOUR_CITY}&amp;amp;ZZZ=END")
if response.body =~ /Begin civil twilight[^0-9]*(d+:d{2} [ap].m.).*Sunrise[^0-9]*(d+:d{2} [ap].m.).*Sunset[^0-9]*(d+:d{2} [ap].m.).*End civil twilight[^0-9]*(d+:d{2} [ap].m.)/m
puts "#{month}/#{day}/#{year}"
puts "Begin Twilight: #{$1}"
puts "Sunrise : #{$2}"
puts "Sunset : #{$3}"
puts "End Twilight : #{$4}"
end
end
You just need to edit the three constants that begin with YOUR_. The id used on the Navy web form is ‘AA’, but they have a comment in the HTML that requests you use a unique id of your own up to 8 characters to help them with tracking. You can find a more complete version of the code in my github profile.
Emacs Goodness
After writing the above Ruby script, I made it executable, ‘chmod +x sunrise.rb’, and placed it in my path so I could write a simple Emacs function to invoke it.
(defun bja-sunrise () "Display sunrise, sunset & twilight information." (interactive) (shell-command "sunrise.rb"))
Imagine my surprise when I invoked the Emacs apropos help ‘C-h a’ to see my newly defined function and discovered that Emacs, naturally, already has several commands to display sunrise/sunset information!
- calendar-mouse-sunrise/sunset
- Show sunrise/sunset times for mouse-selected date.
- calendar-sunrise-sunset
- Local time of sunrise and sunset for date under cursor.
- sunrise-sunset
- Local time of sunrise and sunset for today. Accurate to a few seconds.
It doesn’t, however, display twilight information, so my simple function still has a purpose in life. Emacs is awesome
I saw a post on comp.lang.lisp demonstrating the suitability of Common Lisp for functional programming. The poster asked to see versions in other languages including Ruby, so I thought I’d whip something up. Here’s the original post with description of the problem:
This one was too much fun for words in re how cool it is programming
with Lisp. I would like to see this in Ruby, Clojure, Qi, and
Scheme. The precise fun part tho is typing it all in in the final form
versus dividing the thing up into steps to get intermediate results,
ie, a test of one's mastery of one's language. Non-functional
languages I guess have no choice but to stop and assign temporaries.
Given:
(defparameter *pets*
'((dog ((blab 12)(glab 17)(cbret 82)(dober 42)(gshep 25)))
(cat ((pers 22)(siam 7)(tibet 52)(russ 92)(meow 35)))
(snake ((garter 10)(cobra 37)(python 77)(adder 24)(rattle 40)))
(cow ((jersey 200)(heiffer 300)(moo 400)))))
Write:
(defun digest-tag-population (tag-population pick-tags count)...)
Such that:
(digest-tag-population *pets* '(dog cat snake) 5)
=> ((DOG CBRET 82) (DOG DOBER 42) (CAT RUSS 92) (CAT TIBET 52) (SNAKE
PYTHON 77))
...the rules being:
- consider only the populations of tags (the first symbol in each
sublist) found in the parameter pick-tags, a list
- take only the most populous of the union of the populations
- return (tag name population) of the most populous in this order:
firstly, by position of the tag in pick-tags
second, ie within a tag, in descending order of population
(defun subseq-ex (st e s)
(subseq s st (min e (length s))))
(defun digest-tag-population (tag-population pick-tags count)
(flet ((tagpos (tag) (position tag pick-tags)))
(stable-sort (subseq-ex 0 count
(sort (loop for (tag population) in tag-population
when (tagpos tag)
append (loop for pop in population
collecting (list* tag pop)))
'> :key (lambda (x)
(caddr x))))
'< :key (lambda (x) (tagpos (car x))))))
(defparameter *pets*
'((dog ((blab 12)(glab 17)(cbret 82)(dober 42)(gshep 25)))
(cat ((pers 22)(siam 7)(tibet 52)(russ 92)(meow 35)))
(snake ((garter 10)(cobra 37)(python 77)(adder 24)(rattle 40)))
(cow ((jersey 200)(heiffer 300)(moo 400)))))
#+test
(digest-tag-population *pets* '(dog cat snake) 5)
And here is my Ruby version:
PETS = [
[:dog, [[:blab, 12], [:glab, 17], [:cbret, 82], [:dober, 42], [:gshep, 25]]],
[:cat, [[:pers, 22], [:siam, 7], [:tibet, 52], [:russ, 92], [:meow, 35]]],
[:snake, [[:garter, 10], [:cobra, 37], [:python, 77], [:adder, 24], [:rattle, 40]]],
[:cow, [[:jersey, 200], [:heiffer, 300], [:moo, 400]]]
]
def digest_tag_population tag_population, pick_tags, count
tag_population.select {|e| pick_tags.include?(e[0]) }.
inject([]) {|memo,obj| obj[1].each {|e| memo << [obj[0], e[0], e[1]] }; memo }.
sort {|a,b| b[2] <=> a[2] }[0,count].
sort_by {|e| [ tag_population.map{|p| p[0]}.rindex(e[0]), e[2] * -1] }
end
digest_tag_population(PETS, [:dog, :cat, :snake], 5)
Within the function:
Line 1: select elements that match the pick tags
Line 2: map to a list of tuples of the form [:dog, :blab, 12]
Line 3: sort the list of tuples by population and select the first count of them
Line 4: sort by tag position, population
Output:
[[:dog, :cbret, 82],
[:dog, :dober, 42],
[:cat, :russ, 92],
[:cat, :tibet, 52],
[:snake, :python, 77]]
I think Ruby compares very favorably. What do you think? Feel free to submit a version in another language.

Recent Comments