Rosetta Code/Find unimplemented tasks: Difference between revisions

From Rosetta Code
Content added Content deleted
Line 8: Line 8:
=={{header|AutoHotkey}}==
=={{header|AutoHotkey}}==


The GUI overkill version. Most code is encoding/decoding to/from listview.
The GUI overkill version.
<lang AutoHotkey>
<lang AutoHotkey>
#NoEnv
#NoEnv
Line 136: Line 136:
return, str
return, str
}
}

Enc_XML(str, chars="")
{
StringReplace, str, str, &, &amp;, All
StringReplace, str, str, ", &quot;, All
StringReplace, str, str, ', &apos;, All
StringReplace, str, str, <, &lt;, All
StringReplace, str, str, >, &gt;, All
Loop, Parse, chars
StringReplace, str, str, %A_LoopField%, % "&#" . Asc(A_LoopField) . "`;", All
return, str
}


Enc_Uri(str)
Enc_Uri(str)

Revision as of 18:50, 22 August 2010

Task
Rosetta Code/Find unimplemented tasks
You are encouraged to solve this task according to the task description, using any language you may know.

Given the name of a language on Rosetta Code, find all tasks which are not implemented in that language.

Note: Implementations should allow for fetching more data than can be returned in one request to Rosetta Code.

You'll need to use the Media Wiki API, which you can find out about locally, here, or in Media Wiki's API documentation at, API:Query

AutoHotkey

The GUI overkill version. <lang AutoHotkey>

  1. NoEnv
  2. SingleInstance force

SetBatchLines, -1 SetControlDelay, 0

AutoExec:

 Gui, Add, DDL, x10 y10 w270 r20 vlngStr
 Gui, Add, Button, x290 y10 gShowLng, Show Unimplemented
 Gui, Add, ListView, x10 y36 w400 h300 vcatLst gOnLV, Unimplemented
 Gui, Add, StatusBar, vsbStr, Loading available languages... Please wait.
 Gui, Show, , RosettaCode unimplemented list
 allLng := getList("lng")
 selectStr = Select language...
 GuiControl, , LngStr, |%selectStr%||%allLng%
 SB_SetIcon("user32.dll", 5)
 SB_SetText("Done loading languages. Select from menu.")

Return

ShowLng:

 Gui, Submit, NoHide
 If (lngStr != selectStr)
 {
   SB_SetIcon("user32.dll", 2) ; exclamation
   SB_SetText("Loading unimplemented tasks... Please wait.")
   allTsk := getList("tsk")
   allThis := getList(lngStr)
   Loop, Parse, allTsk, |
   {
     If !InStr(allThis,A_LoopField)
       notImpl .= A_LoopField . "|"
     allTskCnt := A_Index
   }
   StringTrimRight, notImpl, notImpl, 1
   LV_Delete()
   GuiControl, -Redraw, catLst
   Loop, Parse, notImpl, |
   {
     LV_Add("", A_LoopField)
     notImplCnt := A_Index
   }
   GuiControl, +Redraw, catLst
   SB_SetIcon("user32.dll", 5) ; information
   SB_SetText("There are " . notImplCnt . " of " . allTskCnt
     . " tasks unimplemented. Double-click task to open in browser.")
   notImpl =
   notImplCnt = 0
 }

Return

OnLV:

 If (A_GuiEvent = "DoubleClick")
 {
   LV_GetText(rowTxt, A_EventInfo)
   rowTxt := Enc_Uri(rowTxt)
   StringReplace, rowTxt, rowTxt, `%20, _, All
   Run % "http://rosettacode.org/wiki/" . rowTxt
 }

Return

getList(category) {

 fileName = temp.xml 
 If (category = "lng")
 {
   category = Programming_Languages
   fileName = lng.xml
   IfExist, %fileName%
   {
     FileGetTime, modTime, %fileName%
     EnvSub, modTime, %A_Now%, days
     If (modTime < 3) ; reload on older than 3 days
       Goto, GetFileNoDL
   }
   SB_SetIcon("user32.dll", 2)
   SB_SetText("Loading languages... Please wait.")
 }
 If (category = "tsk")
   category = Programming_Tasks
 getFile:
   url := "http://www.rosettacode.org/w/api.php?action=query&list="
         . "categorymembers&cmtitle=Category:" . category
         . "&cmlimit=500&format=xml" . continue
   UrlDownloadToFile, %url%, %fileName%
 getFileNoDL:
   FileRead, data, %fileName%
   pos = 1
   rxp = title="(.*?)"
   While (pos := RegExMatch(data, rxp, title, pos))
   {
     If InStr(title1, "Category:")
       StringTrimLeft, title1, title1, 9
     StringReplace, title1, title1, |, `|, All
     title1 := Dec_XML(UTF82Ansi(title1))
     allTitles .= title1 . "|"
     pos++
   }
   rxp = cmcontinue="(.*?)"
   If RegExMatch(data, rxp, next)
   {
     continue = &cmcontinue=%next1%
     Goto getFile
   }
   StringTrimRight, allTitles, allTitles, 1
 Return allTitles

}


Dec_XML(str) {

  Loop 
     If RegexMatch(str, "S)(&#(\d+);)", dec)
        StringReplace, str, str, %dec1%, % Chr(dec2), All 
     Else If   RegexMatch(str, "Si)(&#x([\da-f]+);)", hex)
        StringReplace, str, str, %hex1%, % Chr("0x" . hex2), All 
     Else 
        Break 
  StringReplace, str, str,  , %A_Space%, All 
  StringReplace, str, str, ", ", All
  StringReplace, str, str, ', ', All 
  StringReplace, str, str, <,   <, All 
  StringReplace, str, str, >,   >, All 
  StringReplace, str, str, &,  &, All
  return, str 

}

Enc_Uri(str) {

  f = %A_FormatInteger% 
  SetFormat, Integer, Hex 
  If RegExMatch(str, "^\w+:/{0,2}", pr) 
     StringTrimLeft, str, str, StrLen(pr) 
  StringReplace, str, str, `%, `%25, All 
  Loop 
     If RegExMatch(str, "i)[^\w\.~%/:]", char) 
        StringReplace, str, str, %char%, % "%" . SubStr(Asc(char),3), All 
     Else Break 
  SetFormat, Integer, %f% 
  Return, pr . str 

}

UTF82Ansi(zString) {

 Ansi2Unicode(zString, wString, 65001) 
 Unicode2Ansi(wString, sString, 0) 
 Return sString 

}

Ansi2Unicode(ByRef sString, ByRef wString, CP = 0) {

 nSize := DllCall("MultiByteToWideChar", "Uint", CP 
   , "Uint", 0, "Uint", &sString, "int", -1 
   , "Uint", 0, "int", 0)
 VarSetCapacity(wString, nSize * 2)
 DllCall("MultiByteToWideChar" 
   , "Uint", CP, "Uint", 0, "Uint", &sString, "int", -1 
   , "Uint", &wString, "int", nSize) 

}

Unicode2Ansi(ByRef wString, ByRef sString, CP = 0) {

 nSize := DllCall("WideCharToMultiByte" 
   , "Uint", CP, "Uint", 0, "Uint", &wString, "int", -1 
   , "Uint", 0, "int", 0, "Uint", 0, "Uint", 0) 
 VarSetCapacity(sString, nSize) 
 DllCall("WideCharToMultiByte" 
   , "Uint", CP, "Uint", 0, "Uint", &wString, "int", -1 
   , "str", sString, "int", nSize, "Uint", 0, "Uint", 0) 

}

GuiClose:

 ExitApp

Return </lang>

Output

Loads a list of all languages. Pick the language you would like to see the unimplemented tasks of, press the button, double click the selected task to launch in default browser. It will download the languages to file and redownload if older than 3 days.

C#

Using JSON (not parsed, just Regex.)

To help demonstrate paging, the cmlimit parameter has been omitted from the search query so that 10 rows are returned by default

<lang csharp>using System; using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; using System.Net;

class Program {

   static List<string> GetTitlesFromCategory(string category) {
       string searchQueryFormat = "http://www.rosettacode.org/w/api.php?action=query&list=categorymembers&cmtitle=Category:{0}&format=json{1}";
       List<string> results = new List<string>();
       string cmcontinue = string.Empty;
       do {
           string cmContinueKeyValue;
           //append continue variable as needed
           if (cmcontinue.Length > 0)
               cmContinueKeyValue = String.Format("&cmcontinue={0}", cmcontinue);
           else
               cmContinueKeyValue = String.Empty;
           //execute query
           string query = String.Format(searchQueryFormat, category, cmContinueKeyValue);
           string content = new WebClient().DownloadString(query);
           results.AddRange(new Regex("\"title\":\"(.+?)\"").Matches(content).Cast<Match>().Select(x => x.Groups[1].Value));
           //detect if more results are available
           cmcontinue = Regex.Match(content, @"{""cmcontinue"":""([^""]+)""}", RegexOptions.IgnoreCase).Groups["1"].Value;                
       } while (cmcontinue.Length > 0);
       return results;
   }
   static string[] GetUnimplementedTasksFromLanguage(string language) {
       List<string> alltasks = GetTitlesFromCategory("Programming_Tasks");
       List<string> lang = GetTitlesFromCategory(language);
       return alltasks.Where(x => !lang.Contains(x)).ToArray();
   }
   static void Main(string[] args) {
       string[] unimpl = GetUnimplementedTasksFromLanguage(args[0]);
       foreach (string i in unimpl) Console.WriteLine(i);
   }

}</lang>

Clojure

This uses a couple of core libraries, and a Java method for URL encoding. <lang clojure>(require

 '[clojure.xml :as xml]
 '[clojure.set :as set])

(import '[java.net URLEncoder])</lang>

The titles-cont function fetches and parses an XML response, and walks over it extracting titles. It also extracts the cmcontinue value, if present. Returns a pair [titles,cmcontinue]. <lang clojure>(defn titles-cont [url]

 (let [docseq (-> url xml/parse xml-seq)]
   ((juxt #(filter string? %), #(-> (filter map? %) first :cmcontinue))
     (for [{:keys [tag attrs content]} docseq :when (#{:cm :query-continue} tag)]
       (if (= tag :cm)
         (attrs :title)
         (-> content first :attrs))))))</lang>

The get-titles function has 1- and 2-argument versions. The former takes the name of a category, composes the appropriate URL query, and calls the 2-argument version. The 2-argument version gets a title list (with possible cmcontinue value), chaining further calls as necessary into the lazy sequence result. <lang clojure>(defn urlencode [s] (URLEncoder/encode s "UTF-8")) (defn param [p v] (str p (urlencode v))) (defn join [sep items] (apply str (interpose sep items)) ; 'join is a core function in Clojure 1.2

(defn get-titles

 ([category]
   (let [urlbase "http://www.rosettacode.org/w/api.php"
         params ["action=query"
                 "list=categorymembers"
                 "format=xml"
                 "cmlimit=200"
                 (param "cmtitle=Category:" category)]
         url (str urlbase "?" (join "&" params)]
     (get-titles url nil)))
 ([url continue-at]
   (let [continue-param (if continue-at (param "&cmcontinue=" continue-at))
         [titles continue] (titles-cont (str url continue-param))]
     (if continue
       (lazy-cat titles (get-titles url continue))
       titles))))</lang>

The unimplemented function gets a set of all titles and of language-implemented titles and returns the difference. It uses future in order to do the necessary URL requests in parallel. <lang clojure>(defn unimplemented [lang-name]

 (let [title-set #(future (apply sorted-set (get-titles %)))
       all-titles (title-set "Programming_Tasks")
       lang-titles (title-set lang-name)]
   (seq (set/difference @all-titles @lang-titles))))

(let [titles (unimplemented "Clojure")]

 (doseq [title titles] (println title))
 (println "count: " (count titles)))

(shutdown-agents)</lang>

E

Using JSON.

<lang e>#!/usr/bin/env rune

  1. NOTE: This program will not work in released E, because TermL is an
  2. imperfect superset of JSON in that version: it does not accept "\/".
  3. If you build E from the latest source in SVN then it will work.
  4. Usage: rosettacode-cat-subtract.e [<lang e>]
  5. Prints a list of tasks which have not been completed in the language.
  6. If unspecified, the default language is E.

pragma.syntax("0.9") pragma.enable("accumulator")

def termParser := <import:org.quasiliteral.term.makeTermParser> def jURLEncoder := <unsafe:java.net.makeURLEncoder>

def urlEncode(text) {

 return jURLEncoder.encode(text, "UTF-8")

}

/** Convert JSON-as-term-tree to the corresponding natural E data structures. */ def jsonTermToData(term) {

   switch (term) {
       # JSON object to E map
       match term`{@assocs*}` {
           return accum [].asMap() for term`@{key :String}: @valueJson` in assocs {
               _.with(key, jsonTermToData(valueJson))
           }
       }
       # JSON array to E list
       match term`[@elements*]` {
           return accum [] for elem in elements { _.with(jsonTermToData(elem)) }
       }
       
       # Literals just need to be coerced
       match lit :any[String, int, float64] {
           return lit
       }
       
       # Doesn't support true/false/null, but we don't need that for this application.
   }

}

def fetchCategoryAccum(name, membersSoFar :Set, extraArgs) {

   stderr.println(`Fetching Category:$name $extraArgs...`)
   
   def categoryAPIResource := <http>[`//rosettacode.org/w/api.php?` +
       `action=query&list=categorymembers&cmtitle=Category:${urlEncode(name)}&` +
       `format=json&cmlimit=500&cmprop=title$extraArgs`]
   
   def members :=
     when (def responseJSON := categoryAPIResource <- getTwine()) -> {
       # stderr.println(`Fetched Category:$name $extraArgs, parsing...`)
       def response := jsonTermToData(termParser(responseJSON))
       # stderr.println(`Parsed Category:$name $extraArgs response, extracting data...`)
       def [
         "query" => ["categorymembers" => records],
         "query-continue" => continueData := null
       ] := response
       def members := accum membersSoFar for record in records { _.with(record["title"]) }
       
       switch (continueData) {
         match ==null { 
           stderr.println(`Got all ${members.size()} for Category:$name.`)
           members
         }
         match ["categorymembers" => ["cmcontinue" => continueParam]] {
           stderr.println(`Fetched ${members.size()} members for Category:$name...`)
           fetchCategoryAccum(name, members, `&cmcontinue=` + urlEncode(continueParam))
         }
       }
   } catch p { throw(p) }
   
   return members

}

def fetchCategory(name) {

 return fetchCategoryAccum(name, [].asSet(), "")

}

  1. Interpret program arguments

def lang := switch (interp.getArgs()) {

 match [lang] { lang }
 match [] { "E" }

}

  1. Fetch categories

when (def allTasks := fetchCategory("Programming_Tasks"),

     def doneTasks := fetchCategory(lang),
     def omitTasks := fetchCategory(lang + "/Omit")
    ) -> {
   
   # Compute difference and report
   def notDoneTasks := allTasks &! (doneTasks | omitTasks)
   println()
   println("\n".rjoin(notDoneTasks.getElements().sort()))

} catch p {

   # Whoops, something went wrong
   stderr.println(`$p${p.eStack()}`)

}</lang>

Haskell

Library: HTTP XML

from HackageDB

<lang haskell>import Network.Browser import Network.HTTP import Network.URI import Data.List import Data.Maybe import Text.XML.Light import Control.Arrow import Data.Char

getRespons url = do

 rsp <- Network.Browser.browse $ do
   setAllowRedirects True
   setOutHandler $ const (return ())   -- quiet
   request $ getRequest url
 return $ rspBody $ snd rsp

replaceWithSpace c = (\x -> if c==x then ' ' else x)

encl = chr 34

unimpTasks lang = do

 allTasks <- getRespons "http://www.rosettacode.org/w/api.php?action=query&list=categorymembers&cmtitle=Category:Programming_Tasks&cmlimit=500&format=xml"
 impl <-  getRespons ( "http://rosettacode.org/json/isb/" ++ lang ++ ".json")
 let langxx = map (map(replaceWithSpace '_')) $ filter (/=",") $ words $ map (replaceWithSpace encl ) $ init $ drop 1 impl
     xml = onlyElems $ parseXML allTasks
     allxx = concatMap (map (fromJust.findAttr (unqual "title")). filterElementsName (== unqual "cm")) xml
 mapM_ putStrLn $ sort $ allxx \\ langxx</lang>

J

Solution: <lang j>require 'strings web/gethttp'

findUnimpTasks=: ('Programming_Tasks' -.&getCategoryMembers ,&'/Omit') ([ #~ -.@e.) getCategoryMembers

getTagContents=: dyad define

 'starttag endtag'=. x
 ('\' -.~ endtag&taketo)&.>@(starttag&E. <@((#starttag)&}.);.1 ]) y

)

NB. RosettaCode Utilities parseTitles=: ('"title":"';'"')&getTagContents parseCMcontinue=:('"cmcontinue":"';'"')&getTagContents getCMcontquery=: ('&cmcontinue=' , urlencode)^:(0 < #)@>@parseCMcontinue

getCategoryMembers=: monad define

 buildqry=. 'action=query&list=categorymembers&cmtitle=Category:' , ,&'&cmlimit=500&format=json'
 url=.'http://www.rosettacode.org/w/api.php'
 uri=. url ,'?', buildqry urlencode y
 catmbrs=. qrycont=. 
 whilst. #qrycont=. getCMcontquery jsondat do.
   jsondat=. gethttp uri , qrycont
   catmbrs=. catmbrs, parseTitles jsondat
 end.
 catmbrs

)</lang>

Example Usage: <lang j> 4{. findUnimpTasks 'J' NB. get first 4 unimplemented tasks for J +-------------+--------------+----------------+------------------------------+ |Active object|Atomic updates|Basic input loop|Call foreign language function| +-------------+--------------+----------------+------------------------------+</lang>

Oz

Library: OzHttpClient

By parsing XML and using an XPath-like mechanism: <lang oz>declare

 [HTTPClient] = {Link ['x-ozlib://mesaros/net/HTTPClient.ozf']}
 [XMLParser] = {Link ['x-oz://system/xml/Parser.ozf']}
 fun {FindUnimplementedTasks Language}
    AllTasks = {FindCategory "Programming Tasks"}
    LangTasks = {FindCategory Language}
 in
    {ListDiff AllTasks LangTasks}
 end
 
 fun {FindCategory Cat}
    CatUrl = "http://www.rosettacode.org/mw/api.php?action=query"
    #"&list=categorymembers"
    #"&cmtitle=Category:"#{PercentEncode Cat}
    #"&cmlimit=500&format=xml"
    fun {Loop CMContinue}
       [_ Doc] = {Parse {GetPage CatUrl#CMContinue}}
       Titles = {XPath Doc
                 [api query categorymembers cm {Attribute title}]}
    in
       case {XPath Doc
             [api 'query-continue' categorymembers {Attribute cmcontinue}]}
       of nil then Titles
       [] [NewCMContinueAtom] then
          NewCMContinue = {PercentEncode {Atom.toString NewCMContinueAtom}}
       in
          {Append Titles
           {Loop "&cmcontinue="#NewCMContinue}}
       end
    end
 in
    {Loop nil}
 end


 %% XPath emulation
 fun {XPath Doc Path}
    P|Pr = Path
 in
    Doc.name = P %% assert
    {FoldL Pr XPathStep [Doc]}
 end
 Nothing = {NewName}
 fun {NotNothing X} X \= Nothing end
 
 fun {XPathStep Elements P}
    if {Atom.is P} then
       {FilteredChildren Elements P}
    elseif {Procedure.is P} then
       {Filter {Map Elements P} NotNothing}
    end
 end

 %% A flat list of all Type-children of all Elements.
 fun {FilteredChildren Elements Type}
    {Flatten
     {Map Elements
      fun {$ E}
         {Filter E.children
          fun {$ X}
             case X of element(name:!Type ...) then true
             else false
             end
          end}
      end}}
 end

 fun {Attribute Attr}
    fun {$ Element}
       case {Filter Element.attributes fun {$ A} A.name == Attr end}
       of [A] then A.value
       else Nothing
       end
    end
 end


 %% GetPage
 Client = {New HTTPClient.urlGET init(inPrms(toFile:false toStrm:true) _)}
 fun {GetPage RawUrl}
    Url = {VirtualString.toString RawUrl}
    OutParams
 in
    {Client getService(Url ?OutParams ?_)}
    OutParams.sOut
 end

 fun {PercentEncode Xs}
    case Xs of nil then nil
    [] X|Xr then
       if {Char.isDigit X} orelse {Member X [&- &_ &.  &~]}
          orelse X >= &a andthen X =< &z
          orelse X >= &z andthen X =< &Z then
          X|{PercentEncode Xr}
       else
          {Append &%|{ToHex2 X} {PercentEncode Xr}}
       end
    end
 end
 
 fun {ToHex2 X}
    [{ToHex1 X div 16} {ToHex1 X mod 16}]
 end
 
 fun {ToHex1 X}
    if X >= 0 andthen X =< 9 then &0 + X
    elseif X >= 10 andthen X =< 15 then &A + X - 10
    end
 end


 %% Parse
 local
    Parser = {New XMLParser.parser init} 
 in
    fun {Parse Xs} {Parser parseVS(Xs $)} end
 end
 fun {ListDiff Xs Ys}
    {FoldL Ys List.subtract Xs}
 end

in

 %% show tasks not implemented in Oz
 {ForAll {FindUnimplementedTasks "Oz"} System.showInfo}</lang>

Perl

Using JSON (not parsed, just Regex.)

<lang perl>use LWP::Simple 'get';

my $fmt = 'http://www.rosettacode.org/w/api.php?action=query&list=categorymembers&cmtitle=Category:%s&cmlimit=500&format=json'; my $lang = shift

   or die "No language given.\n";

sub urlencode

  {join , map {sprintf '%%%02x', ord} split //, shift}

sub tasks

  {my $category = urlencode shift;
   my @tasks;
   my $json = get sprintf $fmt, $category;
   for (;;)
      {push @tasks, $json =~ /"title":"(.+?)"}/g;
       $json =~ /"cmcontinue":"(.+?)"}/ or last;
       $json = get sprintf $fmt . '&cmcontinue=%s',
           $category, urlencode $1;}
   return @tasks;}

my @all = tasks 'Programming_Tasks'; my %lang = map {$_, 1} tasks $lang

   or die "No such category.\n";

$lang{$_} or print "$_\n"

   foreach @all;</lang>

See also: User:ImplSearchBot/Code

Python

Using XML.

<lang python>import xml.dom.minidom import urllib, sys

def findrc(category):

   name = "http://www.rosettacode.org/w/api.php?action=query&list=categorymembers&cmtitle=Category:%s&cmlimit=500&format=xml" % urllib.quote(category)
   cmcontinue, titles = , []
   while True:
       u = urllib.urlopen(name + cmcontinue)
       xmldata = u.read()
       u.close()
       x = xml.dom.minidom.parseString(xmldata)
       titles += [i.getAttribute("title") for i in x.getElementsByTagName("cm")]
       cmcontinue = filter( None,
                            (urllib.quote(i.getAttribute("cmcontinue"))
                             for i in x.getElementsByTagName("categorymembers")) )
       if cmcontinue:
           cmcontinue = '&cmcontinue=' + cmcontinue[0]
       else:
           break
   return titles

alltasks = findrc("Programming_Tasks") lang = findrc(sys.argv[1])

for i in [i for i in alltasks if i not in lang]:

   print i</lang>

R

Library: XML (R)

<lang R>library(XML) find.unimplemented.tasks <- function(lang="R"){ PT <- xmlInternalTreeParse( paste("http://www.rosettacode.org/w/api.php?action=query&list=categorymembers&cmtitle=Category:Programming_Tasks&cmlimit=500&format=xml",sep="") ) PT.nodes <- getNodeSet(PT,"//cm") PT.titles = as.character( sapply(PT.nodes, xmlGetAttr, "title") ) language <- xmlInternalTreeParse( paste("http://www.rosettacode.org/w/api.php?action=query&list=categorymembers&cmtitle=Category:", lang, "&cmlimit=500&format=xml",sep="") ) lang.nodes <- getNodeSet(language,"//cm") lang.titles = as.character( sapply(lang.nodes, xmlGetAttr, "title") ) unimplemented <- setdiff(PT.titles, lang.titles) unimplemented }

  1. Usage

find.unimplemented.tasks(lang="Python") langs <- c("R","python","perl") sapply(langs, find.unimplemented.tasks) # fetching data for multiple languages</lang>

Ruby

Finds tasks that are either not implemented or omitted. Sorts by task creation time.

Uses the RosettaCode module from Count programming examples#Ruby <lang ruby>require 'rosettacode' require 'time'

module RosettaCode

 def self.get_unimplemented(lang)
   programming_tasks = []
   category_members("Programming_Tasks") {|task| programming_tasks << task}
   lang_tasks = []
   category_members(lang) {|task| lang_tasks << task}
   lang_tasks_omit = []
   category_members("#{lang}/Omit") {|task| lang_tasks_omit << task}
   [programming_tasks - lang_tasks, lang_tasks_omit]
 end
 def self.created_time(title)
   url = get_api_url({
     "action" => "query",
     "titles" => title,
     "format" => "xml",
     "rvlimit" => 500,
     "prop" => "revisions",
     "rvprop" => "timestamp"
   })
   doc = REXML::Document.new open(url)
   REXML::XPath.each(doc, "//rev").collect do |node| 
     Time.parse( node.attribute("timestamp").value )
   end.min 
 end

end

puts Time.now lang = ARGV[0] || "Ruby" unimplemented, omitted = RosettaCode.get_unimplemented(lang) unimplemented.collect {|title| [title, RosettaCode.created_time(title)]} .

             sort_by {|e| e[1]} .
             each do |title, date|
               puts "%s %6s %s" % [
                 date.strftime("%Y-%m-%d"), 
                 omitted.include?(title) ? "[omit]" : "" ,
                 title
               ]
             end

</lang>

Output for Ruby

2010-04-05 11:08:07 -0500
2007-01-14        Table creation
2007-01-25 [omit] Pointers and references
2007-02-04        SQL-based authentication
2007-02-09        Metered concurrency
2007-02-27 [omit] Address of a variable
2007-02-27 [omit] Variable size/Set
2007-02-27        Variable size/Get
2007-06-08        OpenGL
2007-06-08        HTTPS/Authenticated
2007-11-06        Pattern matching
2007-11-08 [omit] Parametric polymorphism
2007-11-13        Special characters
2007-12-11        Arithmetic evaluation
2007-12-21 [omit] Proof
2007-12-24        Plot coordinate pairs
2007-12-24        Polynomial regression
2007-12-24        Compare sorting algorithms' performance
2008-03-27        Dragon curve
2008-03-28        Formal power series
2008-09-11        RCRPG
2008-11-02        Active object
2008-11-13        Window creation/X11
2008-12-05 [omit] Constrained genericity
2009-02-17        Rendezvous
2009-03-22        Arena storage pool
2009-05-23        Ray-casting algorithm
2009-05-26 [omit] Memory allocation
2009-05-26        Simulate input/Mouse
2009-05-27        Mouse position
2009-05-27        Keyboard macros
2009-05-27        Window management
2009-05-27        Color of a screen pixel
2009-06-01        HTTPS/Client-authenticated
2009-06-02        Simulate input/Keyboard
2009-06-08 [omit] Create an object at a given address
2009-06-10        Events
2009-06-10        Scope modifiers
2009-08-09        Verify distribution uniformity/Chi-squared test
2009-08-11        Call a function from a foreign language
2009-08-12        Safe addition
2009-12-05        Rate counter
2010-01-02        Loading Animated 3D Data
2010-01-06        Catmull–Clark subdivision surface
2010-01-21        Hough transform
2010-01-25        Compile-time calculation
2010-02-14        Knapsack problem/Bounded
2010-02-21        Deconvolution/1D
2010-02-23        Deconvolution/2D+
2010-02-24        Knapsack problem/Continuous
2010-03-23        Sutherland-Hodgman polygon clipping
2010-03-23        Find Common Directory Path

Tcl

First, find all members of the Programming_Tasks category, then find all members of the $lang category. The difference is the list of unimplemented tasks.

This uses the json and struct::set packages from

Library: tcllib

<lang tcl>package require Tcl 8.5 package require http package require json package require struct::set

fconfigure stdout -buffering none

  1. Initialize a cache of lookups

array set cache {} proc log msg {

   #puts -nonewline $msg

}

proc get_tasks {category} {

   global cache
   if {[info exists cache($category)]} {

return $cache($category)

   }
   set base_url http://www.rosettacode.org/w/api.php
   set query {

action query list categorymembers cmtitle Category:%s format json cmlimit 500

   }
   set query [list {*}$query]; # remove excess whitespace
   set this_query [dict create {*}[split [format $query $category]]]
   set tasks [list]

   while {1} {
       set url [join [list $base_url [http::formatQuery {*}$this_query]] ?]
       while 1 {
           set response [http::geturl $url]

# Process redirects

           if {[http::ncode $response] == 301} {
               set newurl [dict get [http::meta $response] Location]
               if {[string match http://* $newurl]} {
                   set url $newurl
               } else {
                   set url [regexp -inline {http://[^/]+} $url]
                   append url $newurl
               }
               continue
           }

# Check for oopsies!

           if {

[set s [http::status $response]] ne "ok" || [http::ncode $response] != 200 } then {

               error "Oops: url=$url\nstatus=$s\nhttp code=[http::code $response]"
           }
           break
       }

# Get the data out of the message

       set data [json::json2dict [http::data $response]]
       http::cleanup $response

       # add tasks to list
       foreach task [dict get $data query categorymembers] {
           lappend tasks [dict get [dict create {*}$task] title]
       }

       if {[catch {

dict get $data query-continue categorymembers cmcontinue } continue_task]} then {

           # no more continuations, we're done
           break
       }
       dict set this_query cmcontinue $continue_task
   }
   return [set cache($category) $tasks]

}

proc get_unimplemented {lang} {

   set tasks [get_tasks Programming_Tasks]
   set collected [get_tasks Collection_Members]
   set doneTasks [get_tasks $lang]
   set omittedTasks [get_tasks $lang/Omit]
   # Map generic collection task categories to specific ones
   set tasks [regsub -all {Category:(\S+)} $tasks "\\1/$lang"]
   set collectOfLang [struct::set intersect $collected $doneTasks]
   set ignorable [struct::set union $doneTasks $omittedTasks $collectOfLang]
   set unimplemented [struct::set difference $tasks $ignorable]
   puts "\n$lang has [llength $unimplemented] unimplemented programming tasks:"
   if {[llength $unimplemented]} {

puts " [join [lsort $unimplemented] "\n "]"

   }

}

foreach lang {Perl Python Ruby Tcl} {

   get_unimplemented $lang

}</lang>