Rosetta Code/Rank languages by popularity
Sort the most popular computer programming languages based in number of members in Rosetta Code categories.
You are encouraged to solve this task according to the task description, using any language you may know.
- Task
Sample output on 04 May 2021 at 15:50 +02:
Rank: 1 (1,355 entries) Phix Rank: 2 (1,350 entries) Go Rank: 3 (1,347 entries) Julia Rank: 4 (1,325 entries) Raku Rank: 5 (1,279 entries) Perl Rank: 6 (1,237 entries) Python Rank: 7 (1,124 entries) Kotlin Rank: 8 (1,121 entries) C Rank: 9 (1,116 entries) Wren Rank: 10 (1,102 entries) Java ...
- Notes
- Each language typically demonstrates one or two methods of accessing the data:
- with web scraping (via http://www.rosettacode.org/mw/index.php?title=Special:Categories&limit=5000)
- with the API method (examples below for Awk, Perl, Ruby, Tcl, etc).
- The scraping and API solutions can be separate subsections, see the Tcl example.
- Filtering wrong results is optional. You can check against Special:MostLinkedCategories (if using web scraping)
- If you use the API, and do elect to filter, you may check your results against this complete, accurate, sortable, wikitable listing of all 947 programming languages, updated periodically, typically weekly.
- A complete ranked listing of all 798 languages (from the REXX example) is included here ──► output from the REXX program.
Ada
<lang ada>with Ada.Integer_Text_IO; use Ada.Integer_Text_IO; with Ada.Strings.Fixed; use Ada.Strings.Fixed; with Ada.Strings.Unbounded; use Ada.Strings.Unbounded; with Ada.Text_IO; use Ada.Text_IO;
with Ada.Containers.Ordered_Sets; with Ada.Strings.Less_Case_Insensitive;
with AWS.Client; with AWS.Response;
procedure Test is
use Ada.Strings;
function "+" (S : String) return Unbounded_String renames To_Unbounded_String;
type A_Language_Count is record Count : Integer := 0; Language : Unbounded_String; end record;
function "=" (L, R : A_Language_Count) return Boolean is begin return L.Language = R.Language; end "=";
function "<" (L, R : A_Language_Count) return Boolean is begin -- Sort by 'Count' and then by Language name return L.Count < R.Count or else (L.Count = R.Count and then Less_Case_Insensitive (Left => To_String (L.Language), Right => To_String (R.Language))); end "<";
package Sets is new Ada.Containers.Ordered_Sets (A_Language_Count); use Sets;
Counts : Set;
procedure Find_Counts (S : String) is Title_Str : constant String := "title=""Category:"; End_A_Str : constant String := "</a> (";
Title_At : constant Natural := Index (S, Title_Str); begin if Title_At /= 0 then declare Bracket_At : constant Natural := Index (S (Title_At + Title_Str'Length .. S'Last), ">"); End_A_At : constant Natural := Index (S (Bracket_At + 1 .. S'Last), End_A_Str); Space_At : constant Natural := Index (S (End_A_At + End_A_Str'Length .. S'Last), " "); Count : constant Natural := Natural'Value (S (End_A_At + End_A_Str'Length .. Space_At - 1)); Language : constant String := S (Title_At + Title_Str'Length .. Bracket_At - 2); begin if Bracket_At /= 0 and then End_A_At /= 0 and then Space_At /= 0 then begin Counts.Insert (New_Item => (Count, +Language)); exception when Constraint_Error => Put_Line (Standard_Error, "Warning: repeated language: " & Language); -- Ignore repeated results. null; end; end if; -- Recursively parse the string for languages and counts Find_Counts (S (Space_At + 1 .. S'Last)); end; end if;
end Find_Counts;
Place : Natural := 1;
procedure Display (C : Cursor) is begin Put (Place, Width => 1); Put (". "); Put (Element (C).Count, Width => 1); Put (" - "); Put_Line (To_String (Element (C).Language)); Place := Place + 1; end Display; Http_Source : constant AWS.Response.Data := AWS.Client.Get ("http://rosettacode.org/mw/index.php?title=Special:Categories&limit=5000");
begin
Find_Counts (AWS.Response.Message_Body (Http_Source)); Counts.Reverse_Iterate (Display'Access);
end Test; </lang>
ALGOL 68
ALGOL68: using web scraping
Note: the routine http content is currently not available on Win32 systems.
<lang algol68>PROC good page = (REF STRING page) BOOL:
IF grep in string("^HTTP/[0-9.]* 200", page, NIL, NIL) = 0 THEN TRUE ELSE IF INT start, end; grep in string("^HTTP/[0-9.]* [0-9]+ [a-zA-Z ]*", page, start, end) = 0 THEN print (page[start : end]) ELSE print ("unknown error retrieving page") FI; FALSE FI;
MODE LISTOFSTRING = STRUCT(REF LINK first, last, INT upb); MODE LINK = STRUCT(STRING value, REF LINK next);
PRIO LISTINIT = 1; OP LISTINIT = (REF LISTOFSTRING new, REF LINK first)REF LISTOFSTRING: (
new := (first, first, (first IS REF LINK(NIL) | 0 | 1 )); new
);
OP +:= = (REF LISTOFSTRING list, []CHAR item)VOID: (
HEAP LINK new := (STRING(item), REF LINK(NIL)); IF first OF list IS REF LINK(NIL) THEN first OF list := new ELSE next OF last OF list := new FI; last OF list := new; upb OF list +:= 1
);
OP UPB = (LISTOFSTRING list)INT: upb OF list;
OP ARRAYOFSTRING = (LISTOFSTRING list)[]STRING:(
[UPB list]STRING out; REF LINK this := first OF list; FOR i TO UPB list DO out[i] := value OF this; this := next OF this OD; out
);
INT match=0, no match=1, out of memory error=2, other error=3;
PROC re split = (STRING re split, REF STRING beetles)[]STRING:(
LISTOFSTRING out := (NIL, NIL, 0); # LISTINIT REF LINK NIL; # INT start := 1, pos, end; WHILE grep in string(re split, beetles[start:], pos, end) = match DO out +:= beetles[start:start+pos-2]; out +:= beetles[start+pos-1:start+end-1]; start +:= end OD; IF start > UPB beetles THEN out +:= beetles[start:] FI; ARRAYOFSTRING(out) );
IF STRING reply;
INT rc = http content (reply, "www.rosettacode.org", "http://www.rosettacode.org/w/index.php?title=Special:Categories&limit=500", 0); rc /= 0 OR NOT good page (reply)
THEN print (("Error:",strerror (rc))) ELSE
STRING # hack: HTML should be parsed by an official HTML parsing library # re html tag = "<[^>]*>", re a href category = "^<a href=""/wiki/Category:.*"" title=", re members = "([1-9][0-9]* members)";
MODE STATISTIC = STRUCT(INT members, STRING category); FLEX[0]STATISTIC stats;
OP +:= = (REF FLEX[]STATISTIC in out, STATISTIC item)VOID:( [LWB in out: UPB in out+1]STATISTIC new; new[LWB in out: UPB in out]:=in out; new[UPB new]:=item; in out := new );
- hack: needs to be manually maintained #
STRING re ignore ="Programming Tasks|WikiStubs|Maintenance/OmitCategoriesCreated|"+ "Unimplemented tasks by language|Programming Languages|"+ "Solutions by Programming Language|Implementations|"+ "Solutions by Library|Encyclopedia|Language users|"+ "Solutions by Programming Task|Basic language learning|"+ "RCTemplates|Language Implementations";
FORMAT category fmt = $"<a href=""/wiki/Category:"g""" title=""Category:"g""""$; STRING encoded category, category; FORMAT members fmt = $" ("g" members)"$; INT members;
FLEX[0]STRING tokens := re split(re html tag, reply); FOR token index TO UPB tokens DO STRING token := tokens[token index]; FILE file; IF grep in string(re a href category, token, NIL, NIL) = match THEN associate(file, token); make term(file,""""); getf(file, (category fmt, encoded category, category)); close(file) ELIF grep in string(re members, token, NIL, NIL) = match THEN IF grep in string(re ignore, category, NIL, NIL) /= match THEN associate(file, token); getf(file, (members fmt, members)); stats +:= STATISTIC(members, category); close(file) FI FI OD;
OP < = (STATISTIC a,b)BOOL: members OF a < members OF b;
MODE SORTSTRUCT = STATISTIC; PR READ "prelude/sort.a68" PR;
stats := in place shell sort reverse(stats);
INT max = 10; FOR i TO (UPB stats > max | max | UPB stats) DO printf(($g(-0)". "g(-0)" - "gl$,i,stats[i])) OD
FI</lang>
- Sample output:
1. 233 - Python 2. 222 - Ada 3. 203 - OCaml 4. 203 - C 5. 201 - Perl 6. 193 - Haskell 7. 182 - Java 8. 179 - D 9. 178 - ALGOL 68 10. 160 - Ruby
ALGOL 68:using the API
<lang algol68> CHAR line feed = REPR 10, carriage return = REPR 13; STRING crlf = carriage return + line feed; STRING domain = "rosettacode.org",
page = "/mw/api.php?format=xml&action=query&generator=categorymembers&gcmtitle=Category:Programming%20Languages&gcmlimit=500&prop=categoryinfo";
- concatenate tuples #
OP + = ([]STRING a, b) []STRING:
BEGIN [⌈a + ⌈b] STRING c; c[:⌈a] := a; c[⌈a+1:] := b; c END;
- count occurrances of string in string #
PROC count = (STRING sub, str) INT :
BEGIN INT count := 0; IF UPB str ≥ UPB sub AND UPB str ≥ 1 THEN
INT p := 1; INT p0; WHILE p + UPB sub - 1 <= UPB str ANDF (p0 := p; string in string (sub, p, str[p0:])) DO
count +:= 1;
p +:= p0 + UPB sub - 1 OD
FI; count END;
- split string into tuple #
PROC split = (STRING str, sep) FLEX[]STRING :
BEGIN INT seplen = UPB sep, strlen = UPB str; INT cnt := 0, start := 1; INT p; [count (sep, str) + 1] STRING list; WHILE start ≤ strlen - (seplen - 1)
ANDF string in string (sep, p, str[start:]) DO p +:= start - 1; list[cnt +:= 1] := str[start:p-1]; start := p + seplen
OD; IF cnt = 0 THEN list[cnt +:= 1] := str ELIF start ≤ strlen THEN list[cnt +:= 1] := str[start:] ELIF start = strlen + 1 AND seplen ≥ 1 THEN list[cnt +:= 1] := "" FI; list END;
- reverse strings in a TUPLE #
OP REVERSE = ([]STRING org) []STRING :
BEGIN [UPB org]STRING new; FOR i TO UPB org DO
new[UPB org - (i - 1)] := org[i]
OD; new END;
- convert unsigned number to INT #
OP TOINT = (STRING str) INT:
BEGIN INT p := 1, len := UPB str; WHILE p ≤ len ANDF is space (str[p]) DO p +:= 1 OD; IF str[1] = "-" OR str[1] = "+" THEN
p +:= 1
FI; INT n := 0; WHILE p ≤ len ANDF is space (str[p]) DO p +:= 1 OD; FOR i FROM p TO len WHILE is digit (str[i]) DO
n := n × 10 + ABS str[i] - ABS "0"
OD; n END;
- pad to fixed width #
PROC field = (UNION (STRING,INT) x, INT w) STRING:
BEGIN STRING s = (x | (INT i): whole (i,0), (STRING t): t); (w >= UPB s | " " * (w - UPB s)) + s END;
PROC get web page = (STRING host, path) STRING:
BEGIN STRING reply; INT rc; # 'http content' sometimes fails with interrupted system call, so we loop until succeeding # WHILE
# 'http content' makes requests that are not accepted by rosettacode.org, so therefore the hack # STRING hack = " HTTP/1.0" + crlf + "Host: rosettacode.org" + crlf +
"User-Agent: rank_languages_by_popularity"; rc := http content (reply, host, path + hack, 0);
rc = 4
DO SKIP OD; IF rc = 0 AND grep in string ("^HTTP/[0-9.]+ 200", reply, NIL, NIL) = 0 THEN
INT p; IF string in string (crlf + crlf, p, reply) THEN STRING headers = reply[:p], body = reply[p+4:]; body ELSE "" FI
ELSE
print (strerror (rc)); ""
FI END;
- the main program rank languages by popularity starts here #
STRING gcmcontinue; FLEX[0]STRING lines;
- get through API in chunks of 500 #
WHILE
STRING body = get web page (domain, page + (gcmcontinue /= "" | "&gcmcontinue=" + gcmcontinue)); INT b, e; gcmcontinue := (grep in string ("gcmcontinue=""([^""]+)", body, b, e) = 0 | body[b+13:e-1] | ""); # split the XML into lines on </page> # lines := lines + split (body, "</page>"); gcmcontinue /= "" DO SKIP
OD;
- Each line is one language,
go through them and rewrite them to something we can sort #
FOR i TO UPB lines DO
STRING line = lines[i]; STRING title; INT pages := 0; INT b, e; # the two fields we are intrested in are title="Category:xxx", and pages="999" # IF grep in string ("title=""Category:[^""]+""", line, b, e) = 0 THEN title := line[b+16:e-1] FI; IF grep in string ("pages=""[0-9]+""", line, b, e) = 0 THEN pages := TOINT line[b+7:e-1] FI; lines[i] := field (pages, 6) + " " + title
OD;
lines := REVERSE SORT lines;
INT rank := 1; BOOL tied := FALSE, lasttied := FALSE; print ((new line, whole (UPB lines, 0), " languages", new line, new line)); FOR i TO UPB lines DO
INT entries = TOINT lines[i][:6]; STRING lang = lines[i][8:]; IF entries > 0 THEN tied := i < UPB lines ANDF lines[i][:6] = lines[i+1][:6]; print (("rank: ", field (rank,3), " ", (tied OR lasttied | "[tied]" | " "*6), field ("(" + whole (entries,0) + " " + (entries = 1 | "entry)" | "entries)"), 20), " ", lang, new line)); IF NOT tied THEN rank +:= 1 FI; lasttied := tied FI
OD</lang>
- Sample output top 10:
572 languages rank: 1 (883 entries) Tcl rank: 2 (875 entries) Racket rank: 3 (837 entries) Python rank: 4 (800 entries) J rank: 5 (772 entries) Ruby rank: 6 (763 entries) Perl 6 rank: 7 (756 entries) C rank: 8 (742 entries) Go rank: 9 (737 entries) D rank: 10 (707 entries) Perl
AutoHotkey
<lang autohotkey>MembsUrl = http://rosettacode.org/mw/index.php?title=Special:Categories&limit=5000 ValidUrl = http://rosettacode.org/wiki/Category:Programming_Languages WebRequest := ComObjCreate("WinHttp.WinHttpRequest.5.1")
- Get the webpages
WebRequest.Open("GET", MembsUrl),WebRequest.Send() MembsPage := WebRequest.ResponseText WebRequest.Open("GET", ValidUrl),WebRequest.Send() ValidPage := WebRequest.ResponseText
- Replace special characters
StringReplace, MembsPage, MembsPage, ΜC++, µC++, All StringReplace, MembsPage, MembsPage, МК-61/52, MK-61/52, All StringReplace, ValidPage, ValidPage, ΜC++, µC++, All StringReplace, ValidPage, ValidPage, МК-61/52, MK-61/52, All
ValidREx := "s)href=""([^""]+)"" title=""Category:([^""]+)"">(?=.*)"
MembsREx := "title=""Category:(.+?)"">.+?\((\d+) members?\)"
- Iterate through all matches for valid languages
ValidLangs := [], FoundPos := 0 While FoundPos := RegExMatch(ValidPage, ValidREx, Match, FoundPos+1) ValidLangs[Match2] := Match1
- Iterate through all matches for categories with members
MembsLangs := [], Dupes := [], Detected := 0, FoundPos := 0 While FoundPos := RegExMatch(MembsPage, MembsREx, Match, FoundPos+1) { ; If it isn't a valid language or is a duplicate, skip it if !ValidLangs.HasKey(Match1) || Dupes.HasKey(Match1) continue
Dupes.Insert(Match1, true) Detected++
; Initialize this member count if !IsObject(MembsLangs[Match2]) MembsLangs[Match2] := [Match1] else MembsLangs[Match2].Insert(Match1) }
- Sort the languages with the highest member count first
Sorted := [] for Members, Languages in MembsLangs Sorted.Insert(1, [Members, Languages])
- Initialize the GUI
Gui, New, HwndGuiHwnd Gui, Add, Text, w300 Center, %Detected% languages detected Gui, Add, Edit, w300 vSearchText gSearch, Filter languages Gui, Add, ListView, w300 r20 Grid gOpen vMyListView, Rank|Members|Category
- Populate the list view
LV_ModifyCol(1, "Integer"), LV_ModifyCol(2, "Integer"), LV_ModifyCol(3, 186) for Rank, Languages in Sorted for Key, Language in Languages[2] LV_Add("", Rank, Languages[1], Language)
Gui, Show,, Rosetta Code return
Open: if (A_GuiEvent == "DoubleClick") { LV_GetText(Language, A_EventInfo, 3) Run, % "http://rosettacode.org" ValidLangs[Language] } return
Search: GuiControlGet, SearchText GuiControl, -Redraw, MyListView
LV_Delete() for Rank, Languages in Sorted for Key, Language in Languages[2] if InStr(Language, SearchText) LV_Add("", Rank, Languages[1], Language)
GuiControl, +Redraw, MyListView return
GuiClose: ExitApp return</lang>
AWK
By using the API
This is the third solution. The first solution used web scraping with an external program ns for networking. The second solution used the Rosetta Code API instead of web scraping, but continued use of ns which for unknown reasons didn't work correctly. This solution uses native gawk networking to connect to the API at 500 items per request ("gmcontinue").
<lang awk>function join(array, start, end, sep, result, i) {
result = array[start] for (i = start + 1; i <= end; i++) result = result sep array[i] return result
}
function trim(str) {
gsub(/^blank:+|[[:blank:]\n]+$/, "", str) return str
}
function http2var( site,path,server,j,output) {
RS = ORS = "\r\n"
site = "rosettacode.org" path = "/mw/api.php" \ "?action=query" \ "&generator=categorymembers" \ "&gcmtitle=Category:Programming%20Languages" \ "&gcmlimit=500" \ (gcmcontinue "" ? "&gcmcontinue=" gcmcontinue : "") \ "&prop=categoryinfo" \ "&format=txt"
server = "/inet/tcp/0/" site "/80" print "GET " path " HTTP/1.0" |& server print "Host: " site |& server print "" |& server while ((server |& getline) > 0) { if($0 != 0) { j++ output[j] = $0 } } close(server) if(length(output) == 0) return -1 else return join(output, 1, j, "\n")
}
function parse(webpage ,c,a,i,b,e,pages) {
# Check for API continue code ie. a new page of results available match(webpage, "gcmcontinue[]] =>[^)]+[^)]", a) if(a[0] != "") { split(a[0], b, ">") gcmcontinue = trim(b[2]) } else gcmcontinue = ""
c = split(webpage, a, "[[][0-9]{1,7}[]]")
while(i++ < c) { if(match(a[i], /[pages]/)) { match(a[i], "pages[]] =>[^[]+[^[]", b) split(b[0], e, ">") pages = trim(e[2]) + 0 } else pages = 0 if(match(a[i], /[title]/)) { match(a[i], "title[]] =>[^[]+[^[]", b) split(b[0], e, ":") e[2] = trim(e[2]) if ( substr(e[2], length(e[2]), 1) == ")" ) e[2] = trim( substr(e[2], 1, length(e[2]) - 1) ) if(length(e[2]) > 0) G[e[2]] = pages } }
}
BEGIN {
parse( http2var() ) # First 500 while ( gcmcontinue != "" ) parse( http2var() ) # Next 500, etc
# https://www.gnu.org/software/gawk/manual/html_node/Controlling-Scanning.html PROCINFO["sorted_in"] = "@val_type_desc" for ( language in G ) print ++i ". " language " - " G[language]
}</lang>
- Output from 26 May 2015:
1. Tcl - 867 2. Racket - 863 3. Python - 828 4. J - 777 5. Ruby - 769 6. Perl 6 - 755 7. C - 751 ... 570. NQP - 0 571. AspectC++ - 0 572. Cilk - 0 573. PL/M - 0 574. Agda2 - 0
BBC BASIC
Note that language names differing only in their case are merged. <lang bbcbasic> INSTALL @lib$+"SORTLIB"
SortUp% = FN_sortinit(0,0) : REM Ascending SortDown% = FN_sortinit(1,0) : REM Descending VDU 23,22,640;512;8,16,16,128+8 : REM Enable UTF-8 support DIM lang$(1000), tasks%(1000) NORM_IGNORECASE = 1 SYS "LoadLibrary", "URLMON.DLL" TO urlmon% SYS "GetProcAddress", urlmon%, "URLDownloadToFileA" TO UDTF PRINT "Downloading languages list..." url$ = "http://rosettacode.org/wiki/Category:Programming_Languages" file$ = @tmp$ + "languages.htm" SYS UDTF, 0, url$, file$, 0, 0 TO fail% IF fail% ERROR 100, "File download failed (languages)" file% = OPENIN(file$) index% = 0 WHILE NOT EOF#file% REPEAT a$ = GET$#file% IF INSTR(a$, "<a href=""/wiki/Category") = 0 EXIT REPEAT i% = INSTR(a$, "</a>") IF i% = 0 EXIT REPEAT j% = i% REPEAT i% -= 1 : UNTIL MID$(a$,i%,1) = ">" OR i% = 0 IF i% = 0 EXIT REPEAT lang$(index%) = MID$(a$, i%+1, j%-i%-1) IF lang$(index%) <> "Languages" index% += 1 UNTIL TRUE ENDWHILE CLOSE #file% C% = index% CALL SortUp%, lang$(0) PRINT "Downloading categories list..." url$ = "http://www.rosettacode.org/w/index.php" url$ += "?title=Special:Categories&limit=5000" file$ = @tmp$ + "categories.htm" SYS UDTF, 0, url$, file$, 0, 0 TO fail% IF fail% ERROR 100, "File download failed (categories)" file% = OPENIN(file$) WHILE NOT EOF#file% REPEAT a$ = GET$#file% i% = INSTR(a$, "member") IF i% = 0 EXIT REPEAT REPEAT i% -= 1 : UNTIL MID$(a$,i%,1) = "(" OR i% = 0 IF i% = 0 EXIT REPEAT tasks% = VAL(MID$(a$, i%+1)) IF tasks% = 0 EXIT REPEAT REPEAT i% -= 1 : UNTIL MID$(a$,i%,1) = "<" OR i% = 0 IF i% = 0 EXIT REPEAT j% = i% REPEAT i% -= 1 : UNTIL MID$(a$,i%,1) = ">" OR i% = 0 IF i% = 0 EXIT REPEAT k% = FNwhere(lang$(), MID$(a$, i%+1, j%-i%-1), index%-1) IF k% <> -1 tasks%(k%) += tasks% UNTIL TRUE ENDWHILE CLOSE #file% CALL SortDown%, tasks%(0), lang$(0) VDU 14 @% = 3 : REM Column width PRINT "List of languages as of " TIME$ FOR i% = 0 TO index%-1 IF tasks%(i%) = 0 EXIT FOR PRINT i%+1 ". " tasks%(i%) " - " lang$(i%) NEXT END DEF FNwhere(a$(), S$, T%) LOCAL B%, C%, H% H% = 2 WHILE H%<T% H% *= 2:ENDWHILE H% /= 2 REPEAT IF (B%+H%)<=T% THEN SYS "CompareString", 0, NORM_IGNORECASE, S$, -1, a$(B%+H%), -1 TO C% IF C% >= 2 B% += H% ENDIF H% /= 2 UNTIL H%=0 SYS "CompareString", 0, NORM_IGNORECASE, S$, -1, a$(B%), -1 TO C% IF C% = 2 THEN = B% ELSE = -1</lang>
Output:
Downloading languages list... Downloading categories list... List of languages as of Sat.17 Nov 2012,00:21:11 1. 682 - Tcl 2. 638 - Python 3. 626 - PicoLisp 4. 622 - C 5. 592 - J 6. 581 - Go 7. 570 - Ruby 8. 553 - Ada 9. 515 - Perl 10. 514 - D 11. 507 - Haskell 12. 490 - Perl 6 13. 489 - BBC BASIC 14. 477 - Java 15. 473 - Mathematica 16. 469 - PureBasic 17. 469 - OCaml 18. 459 - Unicon 19. 438 - REXX 20. 428 - Icon ...... 461. 1 - ScriptBasic 462. 1 - Qore 463. 1 - Opa 464. 1 - Nickle 465. 1 - Neko 466. 1 - Neat 467. 1 - MEL 468. 1 - MAPPER 469. 1 - Kotlin 470. 1 - Chapel
Bracmat
<lang bracmat> ( get-page
= url type . !arg:(?url.?type) & sys$(str$("wget -q -O wget.out \"" !url \")) & get$("wget.out",!type) { Type can be JSN, X ML, HT ML or just ML. } )
& ( get-langs
= arr lang . :?arr & !arg:? (.h2.) ?arg (h2.?) ? { Only analyse part of page between the h2 elements. } & whl ' ( !arg : ? ( a . ? ( title . @(?:"Category:" ?):?lang & !lang !arr:?arr ) ? ) ?arg ) & !arr )
& ( get-cats
= page langs list count pat li A Z . !arg:(?page.?langs) & 0:?list & whl ' ( !langs:%?lang ?langs & { Use macro substitution to create a fast pattern. } ' ( ? (a.? (title.$lang) ?) { $lang is replaced by the actual language. } ? (.a.) @(?:? #?count " " ?) ) : (=?pat) & ( !page : ?A ( (li.) ?li (.li.) ?Z & !li:!pat ) & !A !Z:?page { Remove found item from page. (Not necessary at all.)} & !count | 0 { The language has no examples. } . ) \L !lang { Bracmat normalizes a\Lx+b\Ly+a\Lz to a\L(x*z)+b\Ly, so } + !list { it's easy to collect categories with the same count. } : ?list ) & !list )
& get-cats
$ ( get-page $ ( "http://www.rosettacode.org/w/index.php?title=Special:Categories&limit=5000" . HT,ML ) . get-langs $ ( get-page $ ( "http://rosettacode.org/wiki/Category:Programming_Languages" . HT ML ) ) ) : ?cats
& :?list & whl
' ( !cats:(?count.)\L?tiedcats+?cats & :?ties & whl ' ( !tiedcats:@(?:"Category:" ?name)*?tiedcats & !ties !name:?ties ) & (!count.!ties) !list:?list )
& 1:?rank & whl
' ( !rank:?tiedRank & !list:(?count.?ties) ?list & whl ' ( !ties:%?name ?ties & @(!tiedRank:? [?len) { We want some padding for the highest ranks. } & @(" ":? [!len ?sp) { Skip blanks up to the length of the rank. } & out$(str$(!sp !tiedRank ". " !count " - " !name)) & 1+!rank:?rank ) )
& ;</lang> Output:
1. 816 - Tcl 2. 771 - Racket 3. 760 - Python 4. 708 - C 5. 705 - Perl 6 6. 700 - J 7. 695 - Ruby 8. 690 - D 9. 656 - Go 10. 643 - PicoLisp 11. 639 - Perl 12. 622 - REXX 13. 602 - Ada 14. 591 - Mathematica 15. 588 - Haskell 16. 574 - AutoHotkey 17. 559 - Unicon 18. 543 - Java 19. 526 - BBC BASIC 20. 510 - Icon 21. 500 - C++ ... 498. 1 - Vox 498. 1 - XPath 2.0 498. 1 - Xanadu 523. 0 - Clarion 523. 0 - EhBASIC 523. 0 - Epigram 523. 0 - FLORA-2 523. 0 - Florid 523. 0 - Jcon 523. 0 - LLP 523. 0 - Lolli 523. 0 - Lygon 523. 0 - Monte 523. 0 - ObjectIcon 523. 0 - RPGIV 523. 0 - Rubylog 523. 0 - Star 523. 0 - True BASIC 523. 0 - X10 523. 0 - XS 523. 0 - Ya
C
Ghetto parser<lang c>#include <stdio.h>
- include <stdlib.h>
- include <string.h>
const char * lang_url = "http://www.rosettacode.org/w/api.php?action=query&" "list=categorymembers&cmtitle=Category:Programming_Languages&" "cmlimit=500&format=json"; const char * cat_url = "http://www.rosettacode.org/w/index.php?title=Special:Categories&limit=5000";
- define BLOCK 1024
char *get_page(const char *url) { char cmd[1024]; char *ptr, *buf; int bytes_read = 1, len = 0; sprintf(cmd, "wget -q \"%s\" -O -", url); FILE *fp = popen(cmd, "r"); if (!fp) return 0; for (ptr = buf = 0; bytes_read > 0; ) { buf = realloc(buf, 1 + (len += BLOCK)); if (!ptr) ptr = buf; bytes_read = fread(ptr, 1, BLOCK, fp); if (bytes_read <= 0) break; ptr += bytes_read; } *++ptr = '\0'; return buf; }
char ** get_langs(char *buf, int *l) { char **arr = 0; for (*l = 0; (buf = strstr(buf, "Category:")) && (buf += 9); ++*l) for ( (*l)[arr = realloc(arr, sizeof(char*)*(1 + *l))] = buf; *buf != '"' || (*buf++ = 0); buf++);
return arr; }
typedef struct { const char *name; int count; } cnt_t; cnt_t * get_cats(char *buf, char ** langs, int len, int *ret_len) { char str[1024], *found; cnt_t *list = 0; int i, llen = 0; for (i = 0; i < len; i++) { sprintf(str, "/wiki/Category:%s", langs[i]); if (!(found = strstr(buf, str))) continue; buf = found + strlen(str);
if (!(found = strstr(buf, "</a> ("))) continue; list = realloc(list, sizeof(cnt_t) * ++llen); list[llen - 1].name = langs[i]; list[llen - 1].count = strtol(found + 6, 0, 10); } *ret_len = llen; return list; }
int _scmp(const void *a, const void *b) { int x = ((const cnt_t*)a)->count, y = ((const cnt_t*)b)->count; return x < y ? -1 : x > y; }
int main() { int len, clen; char ** langs = get_langs(get_page(lang_url), &len); cnt_t *cats = get_cats(get_page(cat_url), langs, len, &clen); qsort(cats, clen, sizeof(cnt_t), _scmp); while (--clen >= 0) printf("%4d %s\n", cats[clen].count, cats[clen].name);
return 0; }</lang>
- Output:
563 Tcl 529 PicoLisp 522 Python 504 C 500 J 442 Go 440 Ruby 435 Ada 430 PureBasic 427 Perl ...
Using cJSON.
Compiled with gcc -lcurl -lm cJSON.c lang_rank.c
Usage: rank [number]
Outputs the first [number] languages in the list, default to 10. Use -1 to display all the languages.
<lang c>
- include <stdio.h>
- include <stdlib.h>
- include <string.h>
- include <curl/curl.h>
- include "cJSON.h"
char *URL_BASE = "http://www.rosettacode.org/mw/api.php?format=json&action=query&generator=categorymembers&gcmtitle=Category:Programming%20Languages&gcmlimit=500&prop=categoryinfo&rawcontinue"; char *URL_BASE_CONT = "http://www.rosettacode.org/mw/api.php?format=json&action=query&generator=categorymembers&gcmtitle=Category:Programming%20Languages&gcmlimit=500&prop=categoryinfo&gcmcontinue=";
typedef struct mem { char *text; size_t size; } mem;
typedef struct page { char *name; int num; } page;
size_t write_callback(void *ptr, size_t size, size_t nmemb, void *userdata); void curl_request(CURL *curl, char *url, mem *response); char *build_url(char *cont); char *get_cont(cJSON *json); void sort_arrays(page *pages, int *s); cJSON *parse_json(cJSON *json); page *fill_arrays(page *pages, int *s, cJSON *json);
int main(int argc, char *argv[]) { curl_global_init(CURL_GLOBAL_ALL); CURL *curl = curl_easy_init(); char *cont = NULL; page *pages = malloc(1); int till = 10; int *npag = malloc(sizeof(int)); *npag = 0; if (argc>1) till = atoi(argv[1]); do { mem *response = calloc(1, sizeof(mem)); char *url = build_url(cont); if (cont) free(cont); curl_request(curl, url, response); cJSON *json = cJSON_Parse(response->text); cont = get_cont(json); cJSON *json_pages = parse_json(json); pages = fill_arrays(pages, npag, json_pages); cJSON_Delete(json); free(url); free(response->text); free(response); } while (cont); sort_arrays(pages, npag); if (till>*npag||till<-1) till=10; if (till==-1) till=*npag; for (int i = 0;i<till;i++) { printf("#%d: %s, %d tasks\n", i+1, pages[i].name, pages[i].num); } for (int i = 0;i<*npag;i++) { free(pages[i].name); } free(pages); free(npag); curl_easy_cleanup(curl); curl_global_cleanup(); return 0; } size_t write_callback(void *ptr, size_t size, size_t nmemb, void *userdata) { mem *response = userdata; response->text = realloc(response->text, response->size+size*nmemb+1); memcpy(&(response->text[response->size]), ptr, size*nmemb); response->size += size*nmemb; response->text[response->size] = '\0'; return size*nmemb; } void curl_request(CURL *curl, char *url, mem *response) { curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, response); curl_easy_perform(curl); } char *build_url(char *cont) { char *url; if (cont) { int size = strlen(URL_BASE_CONT)+strlen(cont)+1; url = calloc(1, size); strncpy(url, URL_BASE_CONT, strlen(URL_BASE_CONT)); strcat(url, cont); } else { url = malloc(strlen(URL_BASE)+1); strcpy(url, URL_BASE); } return url; } cJSON *parse_json(cJSON *json) { cJSON *pages; if (json) { pages = cJSON_GetObjectItem(json, "query"); pages = cJSON_GetObjectItem(pages, "pages"); pages = pages->child; } return pages; } char *get_cont(cJSON *json) { cJSON *jcont = cJSON_GetObjectItem(json, "query-continue"); if (jcont && jcont->child->child) { char *cont = malloc(strlen(jcont->child->child->valuestring)+1); strcpy(cont, jcont->child->child->valuestring); return cont; } else { return NULL; } } page *fill_arrays(page *pag, int *i, cJSON *json) { cJSON *cur_page = json; page *pages = pag; do { pages = realloc(pages, *i*sizeof(page)+sizeof(page)); if (json->child) { int size = strlen(cur_page->child->next->next->valuestring)-9; char *lang = malloc(size+1); strcpy(lang, cur_page->child->next->next->valuestring+9); pages[*i].name = lang; } else { pages[*i].name = "no name"; } int task = cur_page->child->next->next->next?cur_page->child->next->next->next->child->valueint:0; pages[*i].num = task; *i = *i+1; cur_page = cur_page->next; } while (cur_page->next); return pages; } void sort_arrays(page *pages, int *size) { int sorted = 0; do { sorted = 1; for (int i = 0;i<*size-1;i++) { if (pages[i].num<pages[i+1].num) { sorted = 0; int a = pages[i+1].num; pages[i+1].num = pages[i].num; pages[i].num = a; char *s = pages[i+1].name; pages[i+1].name = pages[i].name; pages[i].name = s; } } } while (sorted!=1); } </lang>
- Output:
1. Racket: 907 tasks 2. Tcl: 899 tasks 3. Python: 872 tasks 4. J: 848 tasks 5. Perl 6: 813 tasks 6. Ruby: 796 tasks 7. C: 777 tasks 8. Java: 764 tasks 9. Go: 759 tasks 10. D: 749 tasks
C#
Sorting only programming languages. <lang csharp>using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Net; using System.Text.RegularExpressions;
class Program {
static void Main(string[] args) { string get1 = new WebClient().DownloadString("http://www.rosettacode.org/w/api.php?action=query&list=categorymembers&cmtitle=Category:Programming_Languages&cmlimit=500&format=json"); string get2 = new WebClient().DownloadString("http://www.rosettacode.org/w/index.php?title=Special:Categories&limit=5000");
ArrayList langs = new ArrayList(); Dictionary<string, int> qtdmbr = new Dictionary<string, int>();
MatchCollection match1 = new Regex("\"title\":\"Category:(.+?)\"").Matches(get1); MatchCollection match2 = new Regex("title=\"Category:(.+?)\">.+?</a>[^(]*\\((\\d+) members\\)").Matches(get2);
foreach (Match lang in match1) langs.Add(lang.Groups[1].Value);
foreach (Match match in match2) { if (langs.Contains(match.Groups[1].Value)) { qtdmbr.Add(match.Groups[1].Value, Int32.Parse(match.Groups[2].Value)); } }
string[] test = qtdmbr.OrderByDescending(x => x.Value).Select(x => String.Format("{0,3} - {1}", x.Value, x.Key)).ToArray();
int count = 1;
foreach (string i in test) { Console.WriteLine("{0,3}. {1}", count, i); count++; } }
}</lang>
- Output (as of May 30, 2010):
1. 397 - Tcl 2. 368 - Python 3. 350 - Ruby 4. 333 - J 5. 332 - C 6. 322 - Haskell 7. 322 - OCaml 8. 302 - Perl 9. 290 - Common Lisp 10. 289 - AutoHotkey . . .
Object-oriented solution
<lang csharp>using System; using System.Net; using System.Linq; using System.Text.RegularExpressions; using System.Collections.Generic;
class Category {
private string _title; private int _members;
public Category(string title, int members) { _title = title; _members = members; }
public string Title { get { return _title; } }
public int Members { get { return _members; } }
}
class Program {
static void Main(string[] args) { string get1 = new WebClient().DownloadString("http://www.rosettacode.org/w/api.php?action=query&list=categorymembers&cmtitle=Category:Programming_Languages&cmlimit=500&format=json"); string get2 = new WebClient().DownloadString("http://www.rosettacode.org/w/index.php?title=Special:Categories&limit=5000");
MatchCollection match1 = new Regex("\"title\":\"Category:(.+?)\"").Matches(get1); MatchCollection match2 = new Regex("title=\"Category:(.+?)\">.+?</a>[^(]*\\((\\d+) members\\)").Matches(get2);
string[] valids = match1.Cast<Match>().Select(x => x.Groups[1].Value).ToArray(); List<Category> langs = new List<Category>();
foreach (Match match in match2) { string category = match.Groups[1].Value; int members = Int32.Parse(match.Groups[2].Value);
if (valids.Contains(category)) langs.Add(new Category(category, members)); }
langs = langs.OrderByDescending(x => x.Members).ToList(); int count = 1;
foreach (Category i in langs) { Console.WriteLine("{0,3}. {1,3} - {2}", count, i.Members, i.Title); count++; } }
}</lang>
C++
using g++ under Linux with g++ -lboost_thread -lboost_system -lboost_regex: <lang cpp>#include <string>
- include <boost/regex.hpp>
- include <boost/asio.hpp>
- include <vector>
- include <utility>
- include <iostream>
- include <sstream>
- include <cstdlib>
- include <algorithm>
- include <iomanip>
struct Sort { //sorting programming languages according to frequency
bool operator( ) ( const std::pair<std::string,int> & a , const std::pair<std::string,int> & b ) const {
return a.second > b.second ;
}
} ;
int main( ) {
try { //setting up an io service , with templated subelements for resolver and query boost::asio::io_service io_service ; boost::asio::ip::tcp::resolver resolver ( io_service ) ; boost::asio::ip::tcp::resolver::query query ( "rosettacode.org" , "http" ) ; boost::asio::ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve( query ) ; boost::asio::ip::tcp::resolver::iterator end ; boost::asio::ip::tcp::socket socket( io_service ) ; boost::system::error_code error = boost::asio::error::host_not_found ; //looking for an endpoint the socket will be able to connect to while ( error && endpoint_iterator != end ) {
socket.close( ) ; socket.connect( *endpoint_iterator++ , error ) ;
} if ( error )
throw boost::system::system_error ( error ) ;
//we send a request boost::asio::streambuf request ; std::ostream request_stream( &request ) ; request_stream << "GET " << "/mw/index.php?title=Special:Categories&limit=5000" << " HTTP/1.0\r\n" ; request_stream << "Host: " << "rosettacode.org" << "\r\n" ; request_stream << "Accept: */*\r\n" ; request_stream << "Connection: close\r\n\r\n" ; //send the request boost::asio::write( socket , request ) ; //we receive the response analyzing every line and storing the programming language boost::asio::streambuf response ; std::istream response_stream ( &response ) ; boost::asio::read_until( socket , response , "\r\n\r\n" ) ;
boost::regex e( "
" ) ; //using the wrong regex produces incorrect sorting!! std::ostringstream line ; std::vector<std::pair<std::string , int> > languages ; //holds language and number of examples boost::smatch matches ; while ( boost::asio::read( socket , response , boost::asio::transfer_at_least( 1 ) , error ) ) { line << &response ; if ( boost::regex_search( line.str( ) , matches , e ) ) { std::string lang( matches[2].first , matches[2].second ) ; int zahl = atoi ( lang.c_str( ) ) ; languages.push_back( std::make_pair( matches[ 1 ] , zahl ) ) ; } line.str( "") ;//we have to erase the string buffer for the next read } if ( error != boost::asio::error::eof ) throw boost::system::system_error( error ) ; //we sort the vector entries , see the struct above std::sort( languages.begin( ) , languages.end( ) , Sort( ) ) ; int n = 1 ; for ( std::vector<std::pair<std::string , int> >::const_iterator spi = languages.begin( ) ; spi != languages.end( ) ; ++spi ) { std::cout << std::setw( 3 ) << std::right << n << '.' << std::setw( 4 ) << std::right << spi->second << " - " << spi->first << '\n' ; n++ ; } } catch ( std::exception &ex ) { std::cout << "Exception: " << ex.what( ) << '\n' ; } return 0 ; }</lang>
- Sample output (just the "top ten"):
1. 367 - Tcl 2. 334 - Python 3. 319 - Ruby 4. 286 - C 5. 277 - Perl 6. 272 - OCaml 7. 264 - Ada 8. 241 - E 9. 239 - AutoHotkey 10. 193 - Forth
Caché ObjectScript
<lang cos>Class Utils.Net.RosettaCode [ Abstract ] {
ClassMethod GetTopLanguages(pHost As %String = "", pPath As %String = "", pTop As %Integer = 10) As %Status { // check input parameters If $Match(pHost, "^([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,6}$")=0 { Quit $$$ERROR($$$GeneralError, "Invalid host name.") }
// create http request and get page Set req=##class(%Net.HttpRequest).%New() Set req.Server=pHost Do req.Get(pPath)
// create xml stream with doc type Set xml=##class(%Stream.GlobalCharacter).%New() Set sc=xml.WriteLine("<!DOCTYPE doc_type [") Set sc=xml.WriteLine($Char(9)_"<!ENTITY nbsp ' '>") Set sc=xml.WriteLine($Char(9)_"<!ENTITY amp '&'>") Set sc=xml.WriteLine("]>")
// copy xhtml stream to xml stream Set xhtml=req.HttpResponse.Data Set xhtml.LineTerminator=$Char(10) While 'xhtml.AtEnd { Set line=xhtml.ReadLine() If line["!DOCTYPE" Continue If line["<g:plusone></g:plusone>" { Continue Set line="<g:plusone xmlns:g='http://base.google.com/ns/1.0'></g:plusone>" } Set sc=xml.WriteLine(line) }
// create an instance of an %XML.XPATH.Document Set sc=##class(%XML.XPATH.Document).CreateFromStream(xml, .xdoc) If $$$ISERR(sc) Quit sc
// evaluate following 'XPath' expression Set sc=xdoc.EvaluateExpression("//div[@id='bodyContent']//li", "a[contains(@href, '/Category:')]/ancestor::li", .res)
// iterate through list elements Set array=##class(%ArrayOfDataTypes).%New() Do { Set dom=res.GetNext(.key) If '$IsObject(dom) Quit
// get language name and members Set lang="" While dom.Read() { If 'dom.HasValue Continue If lang="" { If $Locate(dom.Value, "User|Tasks|Omit|attention|operations|Solutions by") Quit Set lang=dom.Value Continue } If dom.Value["members" { Set members=+$ZStrip(dom.Value, "<>P") Set list=array.GetAt(members) Set $List(list, $ListLength(list)+1)=lang Set sc=array.SetAt(list, members) Quit } } } While key'="" If array.Count()=0 Quit $$$ERROR($$$GeneralError, "No languages found.")
// show top entries Write "Top "_pTop_" Languages (as at "_$ZDate($HoroLog, 2)_"):", ! For count=1:1:pTop { Set members=array.GetPrevious(.key) If key="" Quit Write $Justify(count, 3), ". ", key, " - ", $ListToString(members, ", "), ! }
// finished Quit $$$OK }
}</lang>
- Example:
USER>Do ##class(Utils.Net.RosettaCode).GetTopLanguages("www.rosettacode.org", "/mw/index.php?title=Special:Categories&limit=5000") Top 10 Languages (as at 21 Apr 2013): 1. 728 - Tcl 2. 668 - Python 3. 654 - C 4. 630 - J 5. 626 - PicoLisp 6. 595 - D 7. 590 - Ruby 8. 589 - Go 9. 576 - Perl 6 10. 567 - Ada
D
With dmd you need compile like "dmd rosetta_popularity.d -L-lphobos2 -L-lcurl". <lang d>void main() {
import std.stdio, std.algorithm, std.conv, std.array, std.regex, std.typecons, std.net.curl;
immutable r1 = `"title":"Category:([^"]+)"`; const languages = get("www.rosettacode.org/w/api.php?action=query"~ "&list=categorymembers&cmtitle=Category:Pro"~ "gramming_Languages&cmlimit=500&format=json") .matchAll(r1).map!q{ a[1].dup }.array;
auto pairs = get("www.rosettacode.org/w/index.php?" ~ "title=Special:Categories&limit=5000") .matchAll(`title="Category:([^"]+)">[^<]+` ~ `</a>[^(]+\((\d+) members\)`) .filter!(m => languages.canFind(m[1])) .map!(m => tuple(m[2].to!uint, m[1].dup));
foreach (i, res; pairs.array.sort!q{a > b}.release) writefln("%3d. %3d - %s", i + 1, res[]);
}</lang>
- Sample output (top twenty as of 2013-01-24):
1. 717 - Tcl 2. 663 - Python 3. 643 - C 4. 626 - PicoLisp 5. 622 - J 6. 587 - Go 7. 587 - Ruby 8. 585 - D 9. 568 - Perl 6 10. 564 - Ada 11. 554 - Mathematica 12. 535 - Perl 13. 532 - Haskell 14. 514 - BBC BASIC 15. 505 - REXX 16. 491 - Java 17. 478 - OCaml 18. 469 - PureBasic 19. 462 - Unicon 20. 430 - AutoHotkey
Delphi
For safe run, download dlls: libeay32.dll & ssleay32.dll, then put in executable path. <lang Delphi> program Rank_languages_by_popularity;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils, System.Classes, IdHttp, IdBaseComponent, IdComponent, IdIOHandler, IdIOHandlerSocket, IdIOHandlerStack, IdSSL, IdSSLOpenSSL, System.RegularExpressions, System.Generics.Collections, System.Generics.Defaults;
const
AURL = 'https://www.rosettacode.org/mw/index.php?title=Special:Categories&limit=5000'; UserAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36';
type
TPair = record Language: string; Users: Integer; constructor Create(lang, user: string); end;
TPairs = TList<TPair>;
{ TPair }
constructor TPair.Create(lang, user: string); begin
Language := lang; Users := StrToIntDef(user, 0);
end;
function GetFullCode: string; begin
with TIdHttp.create(nil) do begin HandleRedirects := True; Request.UserAgent := UserAgent; IOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(nil); Result := Get(AURL); IOHandler.Free; Free; end;
end;
function GetList(const Code: string): TPairs; var
RegularExpression: TRegEx; Match: TMatch; language, users: string;
begin
Result := TPairs.Create;
RegularExpression.Create('>(?<LANG>[^<,;]*)<\/a>.. \((?<USERS>[,\d]*)'); Match := RegularExpression.Match(Code);
while Match.Success do begin users := Match.Groups.Item['USERS'].Value.Replace(',', ); language := Match.Groups.Item['LANG'].Value;
Result.Add(TPair.Create(language, users)); Match := Match.NextMatch; end;
end;
procedure Sort(List: TPairs); begin
List.Sort(TComparer<TPair>.Construct( function(const Left, Right: TPair): Integer begin result := Right.Users - Left.Users; if result = 0 then result := CompareText(Left.Language, Right.Language); end));
end;
function SumUsers(List: TPairs): Cardinal; var
p: TPair;
begin
Result := 0; for p in List do begin Inc(Result, p.Users); end;
end;
var
Data: TStringList; Code, line: string; List: TPairs; i: Integer;
begin
Data := TStringList.Create; Writeln('Downloading code...');
Code := GetFullCode; data.Clear;
List := GetList(Code);
Sort(List);
Writeln('Total languages: ', List.Count); Writeln('Total Users: ', SumUsers(List)); Writeln('Top 10:'#10);
for i := 0 to List.Count - 1 do begin line := Format('%5dth %5d %s', [i + 1, List[i].users, List[i].language]); Data.Add(line); if i < 10 then Writeln(line); end;
Data.SaveToFile('Rank.txt'); List.Free; Data.Free;
Readln;
end.
</lang>
- Output (as of Jul 27, 2020):
Downloading code... Total languages: 3267 Total Users: 96532 Top 10: 1th 1261 Go 2th 1228 Phix 3th 1221 Julia 4th 1210 Raku 5th 1148 Python 6th 1139 Perl 7th 1090 Kotlin 8th 1053 C 9th 1052 Java 10th 1051 Racket
Erlang
<lang Erlang> -module( rank_languages_by_popularity ).
-export( [task/0] ).
-record( print_fold, {place=0, place_step=1, previous_count=0} ).
task() -> ok = find_unimplemented_tasks:init(), Category_programming_languages = find_unimplemented_tasks:rosetta_code_list_of( "Programming_Languages" ), Programming_languages = [X || "Category:" ++ X <- Category_programming_languages], {ok, {{_HTTP,200,"OK"}, _Headers, Body}} = httpc:request( "http://rosettacode.org/mw/index.php?title=Special:Categories&limit=5000" ), Count_categories = lists:sort( [{Y, X} || {X, Y} <- category_counts(Body, []), lists:member(X, Programming_languages)] ), lists:foldr( fun place_count_category_write/2, #print_fold{}, Count_categories ).
category_counts( "", [[] | Acc] ) -> Acc; category_counts( String, Acc ) -> {Begin, End} = category_count_begin_end( String ), {Category_count, String_continuation} = category_count_extract( String, Begin, End ), category_counts( String_continuation, [Category_count | Acc] ).
category_count_begin_end( String ) -> Begin = string:str( String, "/wiki/Category:" ), End = string:str( string:substr(String, Begin), " member" ), category_count_begin_end( Begin, End, erlang:length(" member") ).
category_count_begin_end( _Begin, 0, _End_length ) -> {0, 0}; category_count_begin_end( Begin, End, End_length ) -> {Begin, Begin + End + End_length}.
category_count_extract( _String, 0, _End ) -> {[], ""}; category_count_extract( String, Begin, End ) -> Category_count = category_count_extract( string:substr(String, Begin, End - Begin) ), {Category_count, string:substr( String, End + 1 )}.
category_count_extract( "/wiki/Category:" ++ T ) -> Category_member = string:tokens( T, " " ), Category = category_count_extract_category( Category_member ), Member = category_count_extract_count( lists:reverse(Category_member) ), {Category, Member}.
category_count_extract_category( [Category | _T] ) -> lists:map( fun category_count_extract_category_map/1, string:strip(Category, right, $") ).
category_count_extract_category_map( $_ ) -> $\s; category_count_extract_category_map( Character ) -> Character.
category_count_extract_count( ["member" ++ _, "(" ++ N | _T] ) -> erlang:list_to_integer( N ); category_count_extract_count( _T ) -> 0.
place_count_category_write( {Count, Category}, Acc ) -> Print_fold = place_count_category_write( Count, Acc ), io:fwrite("~p. ~p - ~p~n", [Print_fold#print_fold.place, Count, Category] ), Print_fold;
place_count_category_write( Count, #print_fold{place_step=Place_step, previous_count=Count}=Print_fold ) -> Print_fold#print_fold{place_step=Place_step + 1}; place_count_category_write( Count, #print_fold{place=Place, place_step=Place_step} ) -> #print_fold{place=Place + Place_step, previous_count=Count}. </lang>
- Sample output (top/last ten as of 2013-05-27):
1. 741 - "Tcl" 2. 676 - "Python" 3. 660 - "C" 4. 638 - "J" 5. 627 - "PicoLisp" 6. 609 - "Perl 6" 6. 609 - "D" 8. 607 - "Racket" 9. 592 - "Ruby" 10. 589 - "Go" ... 454. 1 - "Opa" 454. 1 - "Nickle" 454. 1 - "NewtonScript" 454. 1 - "Neko" 454. 1 - "Neat" 454. 1 - "MEL" 454. 1 - "MAPPER" 454. 1 - "LiveScript" 454. 1 - "Kotlin" 454. 1 - "Jacquard Loom"
F#
<lang fsharp>open System open System.Text.RegularExpressions
[<EntryPoint>] let main argv =
let rosettacodeSpecialCategoriesAddress = "http://www.rosettacode.org/mw/index.php?title=Special:Categories&limit=5000" let rosettacodeProgrammingLaguagesAddress = "http://rosettacode.org/wiki/Category:Programming_Languages"
let getWebContent (url :string) = using (new System.Net.WebClient()) (fun x -> x.DownloadString url)
let regexForTitleCategoryFollowedOptionallyByMembercount = new Regex(""" title="Category: (?<Name> [^"]* ) "> # capture the name of the category ( # group begin for optional part [^(]* # ignore up to next open paren (on this line) \( # verbatim open paren (?<Number> \d+ # a number (= some digits) ) \s+ # whitespace member(s?) # verbatim text members (maybe singular) \) # verbatim closing paren )? # end of optional part """, // " <- Make syntax highlighting happy RegexOptions.IgnorePatternWhitespace ||| RegexOptions.ExplicitCapture) let matchesForTitleCategoryFollowedOptionallyByMembercount str = regexForTitleCategoryFollowedOptionallyByMembercount.Matches(str)
let languages = matchesForTitleCategoryFollowedOptionallyByMembercount (getWebContent rosettacodeProgrammingLaguagesAddress) |> Seq.cast |> Seq.map (fun (m: Match) -> (m.Groups.Item("Name").Value, true)) |> Map.ofSeq
let entriesWithCount = let parse str = match Int32.TryParse(str) with | (true, n) -> n | (false, _) -> -1 matchesForTitleCategoryFollowedOptionallyByMembercount (getWebContent rosettacodeSpecialCategoriesAddress) |> Seq.cast |> Seq.map (fun (m: Match) -> (m.Groups.Item("Name").Value, parse (m.Groups.Item("Number").Value))) |> Seq.filter (fun p -> (snd p) > 0 && Map.containsKey (fst p) languages) |> Seq.sortBy (fun x -> -(snd x))
Seq.iter2 (fun i x -> printfn "%4d. %s" i x) (seq { 1 .. 20 }) (entriesWithCount |> Seq.map (fun x -> sprintf "%3d - %s" (snd x) (fst x))) 0</lang>
Showing top 20 as of 2013-04-02
1. 721 - Tcl 2. 665 - Python 3. 647 - C 4. 626 - PicoLisp 5. 622 - J 6. 588 - Go 7. 588 - Ruby 8. 585 - D 9. 569 - Perl 6 10. 565 - Ada 11. 555 - Mathematica 12. 535 - Perl 13. 533 - Haskell 14. 514 - BBC BASIC 15. 505 - REXX 16. 491 - Java 17. 480 - OCaml 18. 469 - PureBasic 19. 462 - Unicon 20. 430 - AutoHotkey
Go
<lang go>package main
import ( "encoding/xml" "fmt" "io" "io/ioutil" "log" "net/http" "net/url" "regexp" "sort" "strconv" "strings" )
var baseQuery = "http://rosettacode.org/mw/api.php?action=query" + "&format=xml&list=categorymembers&cmlimit=500"
func req(u string, foundCm func(string)) string { resp, err := http.Get(u) if err != nil { log.Fatal(err) // connection or request fail } defer resp.Body.Close() for p := xml.NewDecoder(resp.Body); ; { t, err := p.RawToken() switch s, ok := t.(xml.StartElement); { case err == io.EOF: return "" case err != nil: log.Fatal(err) case !ok: continue case s.Name.Local == "cm": for _, a := range s.Attr { if a.Name.Local == "title" { foundCm(a.Value) } } case s.Name.Local == "categorymembers" && len(s.Attr) > 0 && s.Attr[0].Name.Local == "cmcontinue": return url.QueryEscape(s.Attr[0].Value) } } return "" }
// satisfy sort interface (reverse sorting) type pop struct { string int } type popList []pop
func (pl popList) Len() int { return len(pl) } func (pl popList) Swap(i, j int) { pl[i], pl[j] = pl[j], pl[i] } func (pl popList) Less(i, j int) bool { switch d := pl[i].int - pl[j].int; { case d > 0: return true case d < 0: return false } return pl[i].string < pl[j].string }
func main() { // get languages, store in a map langMap := make(map[string]bool) storeLang := func(cm string) { if strings.HasPrefix(cm, "Category:") { cm = cm[9:] } langMap[cm] = true } languageQuery := baseQuery + "&cmtitle=Category:Programming_Languages" continueAt := req(languageQuery, storeLang) for continueAt != "" { continueAt = req(languageQuery+"&cmcontinue="+continueAt, storeLang) } // allocate slice for sorting s := make(popList, 0, len(langMap))
// get big list of categories resp, err := http.Get("http://rosettacode.org/mw/index.php" + "?title=Special:Categories&limit=5000") if err != nil { log.Fatal(err) } page, err := ioutil.ReadAll(resp.Body) resp.Body.Close()
// split out fields of interest and populate sortable slice
rx := regexp.MustCompile("
- Output on 11 Aug 2014:
1. 832 - Tcl 2. 783 - Racket 3. 774 - Python 4. 733 - Perl 6 5. 729 - J … 506. 1 - Supernova 506. 1 - TestML 506. 1 - Vox 506. 1 - XPath 2.0 506. 1 - Xanadu
(All the final entries are tied for spot 506, there are 530 lines.)
Groovy
<lang groovy>def html = new URL('http://rosettacode.org/mw/index.php?title=Special:Categories&limit=5000').getText([
connectTimeout:500, readTimeout:15000, requestProperties: [ 'User-Agent': 'Firefox/2.0.0.4']])
def count = [:]
(html =~ '').each { match, language, members -> count[language] = (members as int) } count.sort { v1, v2 -> v2.value <=> v1.value }.eachWithIndex { value, index -> println "${index + 1} $value" }</lang> Output:
1 Tcl=766 2 Racket=726 3 Python=712 4 Programming Tasks=695 5 C=681 6 Perl 6=649 ... 48 Groovy=323
Haskell
Haskell: Using the API
<lang haskell>{-# LANGUAGE OverloadedStrings #-}
import Data.Aeson import Network.HTTP.Base (urlEncode) import Network.HTTP.Conduit (simpleHttp) import Data.List (sortBy, groupBy) import Data.Function (on) import Data.Map (Map, toList)
-- Record representing a single language. data Language =
Language { name :: String, quantity :: Int } deriving (Show)
-- Make Language an instance of FromJSON for parsing of query response. instance FromJSON Language where
parseJSON (Object p) = do categoryInfo <- p .:? "categoryinfo"
let quantity = case categoryInfo of Just ob -> ob .: "size" Nothing -> return 0
name = p .: "title"
Language <$> name <*> quantity
-- Record representing entire response to query. -- Contains collection of languages and optional continuation string. data Report =
Report { continue :: Maybe String, languages :: Map String Language } deriving (Show)
-- Make Report an instance of FromJSON for parsing of query response. instance FromJSON Report where
parseJSON (Object p) = do querycontinue <- p .:? "query-continue"
let continue = case querycontinue of Just ob -> fmap Just $ (ob .: "categorymembers") >>= ( .: "gcmcontinue") Nothing -> return Nothing
languages = (p .: "query") >>= (.: "pages")
Report <$> continue <*> languages
-- Pretty print a single language showLanguage :: Int -> Bool -> Language -> IO () showLanguage rank tie (Language languageName languageQuantity) =
let rankStr = show rank in putStrLn $ rankStr ++ "." ++ replicate (4 - length rankStr) ' ' ++ (if tie then " (tie)" else " ") ++ " " ++ drop 9 languageName ++ " - " ++ show languageQuantity
-- Pretty print languages with common rank showRanking :: (Int, [Language]) -> IO () showRanking (ranking, languages) =
mapM_ (showLanguage ranking $ length languages > 1) languages
-- Sort and group languages by rank, then pretty print them. showLanguages :: [Language] -> IO () showLanguages allLanguages =
mapM_ showRanking $ zip [1..] $ groupBy ((==) `on` quantity) $ sortBy (flip compare `on` quantity) allLanguages
-- Mediawiki api style query to send to rosettacode.org queryStr = "http://rosettacode.org/mw/api.php?" ++
"format=json" ++ "&action=query" ++ "&generator=categorymembers" ++ "&gcmtitle=Category:Programming%20Languages" ++ "&gcmlimit=100" ++ "&prop=categoryinfo"
-- Issue query to get a list of Language descriptions runQuery :: [Language] -> String -> IO () runQuery ls query = do
Just (Report continue langs) <- decode <$> simpleHttp query let accLanguages = ls ++ map snd (toList langs)
case continue of -- If there is no continue string we are done so display the accumulated languages. Nothing -> showLanguages accLanguages
-- If there is a continue string, recursively continue the query. Just continueStr -> do let continueQueryStr = queryStr ++ "&gcmcontinue=" ++ urlEncode continueStr runQuery accLanguages continueQueryStr
main :: IO () main = runQuery [] queryStr</lang>
- Output:
(As of 2015-07-29.) Here we show only the top 30.
1. Tcl - 887 2. Racket - 877 3. Python - 853 4. J - 795 5. Ruby - 775 6. Perl 6 - 766 7. C - 757 8. Go - 746 9. D - 740 10. Perl - 710 11. REXX - 697 12. PicoLisp - 692 13. Haskell - 682 14. Mathematica - 675 15. Java - 652 16. Zkl - 634 17. Ada - 623 18. AutoHotkey - 591 19. Unicon - 581 20. C++ - 562 21. Common Lisp - 551 22. Scala - 548 23. BBC BASIC - 532 24. Icon - 523 25. C sharp - 516 26. OCaml - 508 27. Nim - 502 28. (tie) Clojure - 485 28. (tie) PureBasic - 485 29. Erlang - 455 30. PARI/GP - 441
Haskell: Using web scraping
Scraping the languages and categories pages. <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.Ord
getRespons url = do
rsp <- Network.Browser.browse $ do setAllowRedirects True setOutHandler $ const (return ()) -- quiet request $ getRequest url return $ rspBody $ snd rsp
mostPopLang = do
rsp <-getRespons $ "http://www.rosettacode.org/w/api.php?action=query&list=" ++
"categorymembers&cmtitle=Category:Programming_Languages&cmlimit=500&format=xml"
mbrs <- getRespons "http://www.rosettacode.org/w/index.php?title=Special:Categories&limit=5000" let xmls = onlyElems $ parseXML rsp langs = concatMap (map ((\\"Category:"). fromJust.findAttr (unqual "title")). filterElementsName (== unqual "cm")) xmls
let catMbr = second (read.takeWhile(/=' '). drop 6). break (=='<'). drop 1. dropWhile(/='>') . drop 5 catNmbs :: [(String, Int)]
catNmbs = map catMbr $ filter (isPrefixOf "
- First 20:
*Main> mostPopLang 1. 421 Tcl 2. 392 Python 3. 365 PicoLisp 4. 363 J 5. 360 Ruby 6. 354 C 7. 344 Haskell 8. 337 OCaml 9. 316 Perl 10. 308 PureBasic 11. 302 AutoHotkey 12. 299 Common Lisp 13. 295 D 14. 295 Java 15. 293 Ada 16. 278 Oz 17. 260 R 18. 259 C sharp 19. 257 C++ 20. 255 ALGOL 68
HicEst
<lang hicest>CHARACTER cats*50000, catlist*50000, sortedCat*50000, sample*100 DIMENSION RankNr(1)
READ(ClipBoard) cats catlist = ' ' pos = 1 ! find language entries like * 100 doors (2 members) nr = 0 ! after next '*' find next "name" = '100 doors' and next "(...)" = '(2 members)' :
1 EDIT(Text=cats, SetPos=pos, Right='*', R, Mark1, R='(', Left, M2, Parse=name, R=2, P=members, GetPos=pos)
IF(pos > 0) THEN READ(Text=members) count IF(count > 0) T