Permalänk
Medlem

Progblemet #2

Tycker att det är dags för nästa progblem.

Dagens progblem:

Citat:

Om vi listar alla naturliga tal under 10, som är multiplikationer av 3 eller 5, får vi 3, 5, 6 och 9. Summan av dessa multiplikationer är 23.

Räkna ut summan av alla multiplikationer av 3 och 5 under 1000.

(från Euler 1)

Svaret är: 233168

-----

Förra tråden fick mig att börja lära mig clojure. Så är fortfarande nybörjare och lösningen är kanske inte helt optimal. Optimera gärna ni som kan clojure.

Clojure - bruteforce

(defn sum-multiplies [up-to] ; En funktion, tar argumentet up-to (reduce + ; Plussa ihop alla tal i listan (tex (reduce + (1 2 3)) -> 6) (filter ; Filtrera bort tal (fn [i] ; Annonym funktion (or (= 0 (mod i 3)) ; Om talet går att dela med 3.. (= 0 (mod i 5)))) ; ..eller 5 så är det ett tal vi letar efter. (filter) låter då talet vara kvar i listan. (range 1 up-to)))) ; Skapar en lista från 1 till up-to. (println (sum-multiplies 1000))

Dold text
Visa signatur

Programmerare -> PHP | HTML | CSS | JS | Java.

Permalänk
Datavetare

Hade precis gjort ett nytt progblemet
Har varit rätt stressigt på jobbet hela vecka så har inte haft tid innan.

Väntar några dagar med progblemet #3 (som inte kommer från project Euler utan lite mer likt något man skulle få som uppgift i skolan) då, ska titta på detta nu.

Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer

Permalänk
Datavetare

Elisp

Då jag antar att ingen annan kommer välja detta språk så kommer här en version för Emacs-lisp.

; Denna version är "vanlig" emacs-lisp som saknar en "range" ; funktion, så jag har skapat en som fungerar som i Clojure (defun range (from to) "Returns a list with integers [from..to)" (let ((lst '()) (n to)) (while (not (= from n)) (setq n (1- n)) (setq lst (cons n lst))) lst)) ; Denna funktion fungerar på följande sätt ; 'apply' är en funktion som kommer applicera en funktion, i detta fall +, på alla element i listan som ges som argument 2 ; Funktionen + ska inte evalueras innan den skickas som argument och ' (quote) ser till detta ; Det som ska summeras är resultatet av funktionen 'mapcar' som kör en funktion (första argumentet) på alla element i en lista (argument 2) ; I detta fall har vi en anonym funktion som i emacs-lisp (och de flesta andra Lisp dialekter) skapas med 'lambda' ; Den anonyma funktionen tar ett argument som här kallas 'n' ; Den anonyma funktionen ger resultatet av 'n' om 'n' är jämt delbart med 3 eller 5 annars blir resultatet 0 ; Denna funktion körs på alla heltal från 1 till up-to minus 1 då (range a b) ger en lista med tal från a till b-1. (defun sum-multiplies (up-to) (apply '+ (mapcar (lambda (n) (if (or (zerop (% n 3)) (zerop (% n 5))) n 0)) (range 1 up-to)))) (insert (format "\n%d" (sum-multiplies 1000))) ; Kör genom att ställa dig på denna rad och skriv ; \M-x eval-buffer

Dold text
Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer

Permalänk
Datavetare

Haskell

Och då jag gissar att Haskell lär ha en av de absolut kortaste lösningar på detta problem så kommer en version i detta språk.

sum_multiplies up_to = foldr (+) 0 [ n | n <- [1..(up_to-1)], (n `mod` 3) == 0 || (n `mod` 5) == 0] main = do print (sum_multiplies 1000)

Dold text

Beskrivning av implementation

Funktioner i Haskell har formen
add_arg1_and_arg2 arg1 arg2 = arg1 + arg2
Detta definierar en funktion med namnet add_arg1_and_arg2 vars formella parametrar är arg1 och arg2. Resultatet av funktionen är värdet av arg1 + arg2

Listor har formen [1,2,3] en lista med tal från N till M kan skrivas [N..M].
Man kan också applicera en funktion på varje element i en lista och på så sätt skapa en ny lista genom denna syntax
[ x * x | x <- [1,2,3] ]
detta kommer skapa listan [1*1,2*2,3*3] -> [1,4,9]

Denna syntax kan också kombineras med ett test varje element måste passera för att tas med i den nya listan. Anta att vi har en funktion odd n som returnerar true om n är udda.
[ x + 11 | x <- [1,2,3], odd x ]
blir då
[1 + 11, 3 + 11] -> [12,14]

(+) i Haskell är en speciell syntax för infix funktioner (namnet på funktionen står mellan 1:a och 2:a argumentet), + används ju normalt som <arg1> + <arg2> så för att referera till denna funktion utan argument måste jag skriva (+).

Man kan också göra det omvända, en vanlig prefix funktion kan användas i infix position om man omsluter namnet med '`' (bakåtapostrof). Jag använde detta ovan för funktionen mod som normalt används så här mod a b vilket är exakt samma sak som a `mod` b

Edit: oops, glömde beskriva foldr
Denna funktion har formen
foldr <fun> <init_val> <lista>
vilket skrivs på detta sätt i Haskell
foldr :: (a -> b -> b) -> b -> [a] -> b

Detta betyder att som första argument tar foldr en funktion som i sin tur tar två argument av typ a och typ b (a kan vara samma som b, vilket är fallet i koden ovan) och producerar ett resultat av typ b. I koden ovan är denna funktion (+) som adderar sina två argument.
Som 2:a argument tar foldr ett startvärde av typ b. Detta är 0 i exemplet ovan och används som argument när listan blir tom.
Som 3:e argument tar foldr en lista av typ a, detta är listan med tal som är jämt delbar med 3 eller 5 ovan.
Som resultat får man resultatet att applicera funktionen på varje element i listan.

Ex: foldr (*) 1 [2,3,4] blir detta
2 * foldr (*) 1 [3,4]
2 * 3 * foldr (*) 1 [4]
2 * 3 * 4 * foldr (*) 1 []
2 * 3 * 4 * 1
24

Dold text

Haskell är verkligen en språk jag kan rekommendera till alla som vill lära sig funktionell programmering. Detta är en av de få "rena" funktionella språk i det att programmet kommer inte kompilera om man gör sidoeffekter i vanliga funktioner. Alla sidoeffekter måste ske i en speciell syntax som ni ser i definitionen av main.

Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer

Permalänk
Medlem

C# konsolapplikation.

using System; namespace Euler1 { class Program { static void Main(string[] args) { int sum = 0; for (int i = 1; i < 1000; i++) if (i % 3 == 0 || i % 5 == 0) sum += i; System.Console.WriteLine(sum); } } }

Dold text
Permalänk
Medlem
Skrivet av Sony?:

Tycker att det är dags för nästa progblem.

Dagens progblem:

(från Euler 1)

Svaret är: 233168

-----

Förra tråden fick mig att börja lära mig clojure. Så är fortfarande nybörjare och lösningen är kanske inte helt optimal. Optimera gärna ni som kan clojure.

Clojure - bruteforce

(defn sum-multiplies [up-to] ; En funktion, tar argumentet up-to (reduce + ; Plussa ihop alla tal i listan (tex (reduce + (1 2 3)) -> 6) (filter ; Filtrera bort tal (fn [i] ; Annonym funktion (or (= 0 (mod i 3)) ; Om talet går att dela med 3.. (= 0 (mod i 5)))) ; ..eller 5 så är det ett tal vi letar efter. (filter) låter då talet vara kvar i listan. (range 1 up-to)))) ; Skapar en lista från 1 till up-to. (println (sum-multiplies 1000))

Dold text

Scala:

(for (n <- 1 until 1000 if n % 3 == 0 || n % 5 == 0) yield n).sum

Dold text

eller

1 until 1000 filter (n => n % 3 * n % 5 == 0) sum

Dold text

edit
Förloppet är ungefär detsamma som för de andra funktionella lösningarna men här är en enkel förklaring:

1 until 1000 En range från 1 till och med 999. filter (n => n % 3 * n % 5 == 0) Filtrera ut de tal som är jämnt delbara med 3 eller 5 sum Summera.

Dold text
Visa signatur

Kom-pa-TI-bilitet

Permalänk

Python:

sum([v for v in range(1000) if v % 3 == 0 or v % 5 == 0])

Dold text
Permalänk
Medlem

Det går att förenkla haskell-lösningen lite

sum [v | v <- [0..999], v `mod` 3 * v `mod` 5 < 1]

Dold text
Visa signatur

AK47s for everyone! - Angry mob
Since NaN /= NaN, I think, we should decipher 'NaN' as 'Not a NaN' - Miguel Mitrofanov
(Varför är människan så benägen att tro på Gud?) Antagligen har det lönat sig och evolutionen har drivit fram sådana hjärnor. - Anon

Permalänk
Medlem

C++ utan modulus

#include <iostream> using namespace std; int calc_multipliers(int number){ int sum = 0; for(int i = number;i < 1000;i+=number){ sum+=i; } return sum; } int main(){ cout << calc_multipliers(3)+calc_multipliers(5) - calc_multipliers(15); return 0; }

Permalänk
Medlem

SQL (MS SQL, otestat):

select sum(number) from master..spt_values where type = 'P' and number < 1000 and (number % 3 * number % 5 = 0)

Dold text
Visa signatur

AK47s for everyone! - Angry mob
Since NaN /= NaN, I think, we should decipher 'NaN' as 'Not a NaN' - Miguel Mitrofanov
(Varför är människan så benägen att tro på Gud?) Antagligen har det lönat sig och evolutionen har drivit fram sådana hjärnor. - Anon

Permalänk
Datavetare

Bash

sum=0; for x in $(seq 1 999); do if [ $(($x % 3 == 0 || $x % 5 == 0)) != 0 ]; then sum=$(($sum + $x)); fi; done; echo $sum

Beskrivning.
Sätt miljövariabeln sum till 0
Generera sekvensen 1 till 999 genom att skriva det till stdout, ett nummer per rad
Gå igenom varje rad (for loopen)
Behandla det som står på varje rad som ett heltal och om det är jämt delbart med 3 eller 5, addera talet till sum
Skriv ut innehållet i sumstdout med echo

Dold text

C# med hjälp av LINQ

Console.WriteLine(Enumerable.Range(0, 1000).Where(n => n % 3 == 0 || n % 5 == 0).Sum());

Beskrivning:
Enumerable.Range(0, 1000) genererar en ström med tal från 0 till 999
Where(n => n % 3 == 0 || n % 5 == 0) behåller de tal som är jämt delbara med 3 och 5
Sum() summerar alla dessa tal

Dold text

Personligen tycker jag LINQ är en riktigt trevlig finess i C#, men valet av namn på funktionerna är totalt korkade. Typ alla andra kallar "Where" för "Filter", "Select" låter som något som väljer ut vissa element, men i stället är det exakt samma sak som det alla andra kallar "Map", alla som någonsin jobbat med funktionella språk vet vad "reduce" alt. "fold" gör, LINQ kallar i stället detta för "Aggregate".
Denna usla namngivning gör LINQ väldigt frustrerande att jobba med, men kanske ett mindre problem för personer som bara jobbar i ett enda språk.

Clojure med hjälp av macro ->> som ger samma typ av flöde som Haskell, Scala, C#/Linq lösningarna ovan

(->> (range 1000) (filter (fn [n] (or (zero? (mod n 3)) (zero? (mod n 5))))) (reduce +))

Beskrivning:
->> tar sitt första argument och evaluerar detta, i detta fall (range 0 1000) som producerar en lista med tal från 0 till 999
->> stoppar sedan i resultatet från rad N som sista argument i rad N+1, så listan ovan stoppas in i filter som tar bort alla element som inte är jämt delbar med 3 eller 5
->> stoppar sedan in resultatet från filter som sista argument till reduce som applicerar funktionen + på alla element
När ->> kommit till sista raden returneras värdet av den beräkningen.

Dold text

Och sedan till den variant som är snabbast av dem alla, C

#include <stdio.h> int sum_multiplies(int sum, int n) { if (n == 0) return sum; return sum_multiplies(sum + (!(n % 3) || !(n % 5) ? n : 0), n - 1); } int main() { printf("%d\n", sum_multiplies(0, 999)); }

Dold text
Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer

Permalänk
Medlem
Skrivet av Yoshman:

Clojure med hjälp av macro ->> som ger samma typ av flöde som Haskell, Scala, C#/Linq lösningarna ovan

(->> (range 1000) (filter (fn [n] (or (zero? (mod n 3)) (zero? (mod n 5))))) (reduce +))

Beskrivning:
->> tar sitt första argument och evaluerar detta, i detta fall (range 0 1000) som producerar en lista med tal från 0 till 999
->> stoppar sedan i resultatet från rad N som sista argument i rad N+1, så listan ovan stoppas in i filter som tar bort alla element som inte är jämt delbar med 3 eller 5
->> stoppar sedan in resultatet från filter som sista argument till reduce som applicerar funktionen + på alla element
När ->> kommit till sista raden returneras värdet av den beräkningen.

Dold text

Nice

Borde man inte kunna göra clojure versionen ännu kortare?

Tänker på att "(zero? (mod n " upprepas två gånger. Tycker att det borde räcka med en, om man får inte det på nå något smart sätt.

Visa signatur

Programmerare -> PHP | HTML | CSS | JS | Java.

Permalänk
Medlem

[QUOTE=Yoshman;12745338]
...
Och sedan till den variant som är snabbast av dem alla, C

#include <stdio.h> int sum_multiplies(int sum, int n) { if (n == 0) return sum; return sum_multiplies(sum + (!(n % 3) || !(n % 5) ? n : 0), n - 1); } int main() { printf("%d\n", sum_multiplies(0, 999)); }

Dold text

[/QUOTE]

Kommer C verkligen vara märkbart snabbare i detta fall? Det allokeras inga nya objekt så där finns ingen GC-overhead.

Visa signatur

Kom-pa-TI-bilitet

Permalänk
Medlem

C++11, med lambda closure!

int sum = 0; [&] (int i) { while (--i) {sum += i*( !(i%3) || !(i%5) );} }(1000);

Dold text
Permalänk
Medlem

[QUOTE=Yoshman;12745338]
Och sedan till den variant som är snabbast av dem alla, C

#include <stdio.h> int sum_multiplies(int sum, int n) { if (n == 0) return sum; return sum_multiplies(sum + (!(n % 3) || !(n % 5) ? n : 0), n - 1); } int main() { printf("%d\n", sum_multiplies(0, 999)); }

Dold text

[/QUOTE]

Den är inte särskilt snabb :P, min c++ implementation var ungefär 10 gånger så snabb

Permalänk
Datavetare

Ruby

Skrivet av Teknocide:

Kommer C verkligen vara märkbart snabbare i detta fall? Det allokeras inga nya objekt så där finns ingen GC-overhead.

Nä, C kommer inte vara snabbar en andra lösningar som undviker temporära objekt. JVM/CLR implementationer får nog ändå svårt att matcha C i detta fall då kostnaden för att starta VM:en och göra JIT-warm-up kommer ta långt mycket längre tid än vad C behöver för att färdigställa hela resultatet.

Testa att skriv "Hello world" i C, Java och C# och mät sedan tiden att köra programmet. Totalt sett kommer det ta <1ms för C men det tar runt 50ms på min dator att köra det enklast möjliga program under Java.

Tar ~1ms att köra detta exempel och då är nog "printf" det dyraste som körs

$ time ./euler1 233168 real 0m0.001s user 0m0.000s sys 0m0.000s

Dold text

Och när jag ändå skriver ett inlägg, här är en version i Ruby för att kompensera för Python versionen. Verkar finnas rätt mycket rivalitet mellan Python och Ruby anhängare (fast skulle jag ställa mig på någon sida i det "kriget" så är det nog på Pythons sida)

(1..999).select{|x| x%3==0 || x%5==0}.inject(0){|sum,n| sum+n}

Dold text
Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer

Permalänk
Skrivet av hawy:

C++ utan modulus

Snyggt!

Liknande variant i Python:

sum(range(0, 1000, 3)) + sum(range(0, 1000, 5)) - sum(range(0, 1000, 15))

Dold text
Permalänk
Medlem
Skrivet av vb:

Det går att förenkla haskell-lösningen lite

sum [v | v <- [0..999], v `mod` 3 * v `mod` 5 < 1]

Dold text

Smart!

Ännu kortarare Clojure med den varianten:

(reduce + (filter (fn [n] (= 0 (* (mod n 3) (mod n 5)))) (range 1 1000)))

Dold text
Visa signatur

Programmerare -> PHP | HTML | CSS | JS | Java.

Permalänk
Datavetare
Skrivet av hawy:

Den är inte särskilt snabb :P, min c++ implementation var ungefär 10 gånger så snabb

Då har du glömt att slå på optimeringar, vilket definitivt kommer göra min implementation långsam då C kompilatorer inte optimerar svansrekursion för debug-byggen då detta gör step-by-step debuggning omöjligt. Utan svansrekursion så måste ju C-versionen skapa och riva ner 1000 st stackramar och där har du nog din faktor 10

Bygger jag både din och min version med gcc/g++ med -O3 och dumpar ut assemblern så ser det mer eller mindre identiskt ut. Båda versionerna går också mer eller mindre exakt lika snabbt, tar runt 16k CPU-cykler att köra på en Core i7 2600k.

Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer

Permalänk
Skrivet av Yoshman:

Då har du glömt att slå på optimeringar

Men din version gör två modulo-operationer för varje nummer, och en modulo är lika dyrt som en division, så hans version borde vara snabbare?

Permalänk
Datavetare
Skrivet av VirtualIntent:

Men din version gör två modulo-operationer för varje nummer, och en modulo är lika dyrt som en division, så hans version borde vara snabbare?

Båda versionerna gör rent logiskt en eller två modulo-operationer. Detta är ju samma kod logiskt sett:
!(n % 3) || !(n % 5)
!(i%3) || !(i%5).
Och i båda fallen räcker det att evaluera !(n%3) om resultatet != 0 och C/C++ MÅSTE utföra short-circut evaluation.

Tittar man sedan i assemblern som gcc 4.6 genererar så ser man att den applicerat något trick så man inte behöver göra divisionen just med talen 3 och 5. I stället används "imul" (integer multiplication) + "sar" (signed shift right). Rent teoretiskt skulle C versionen kunna vara snabbare på CPU:er sedan Nehalem då det finns optimeringar i denna CPU som gör att jämförelse mot 0 inte ens behöver gå via en exekveringsport. Men det verkar som g++ skriver om koden för C++11 varianten så att den blir likna snabb alt. så är vinsten för detta så liten att det helt enkelt inte går att mäta i detta exempel.

Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer

Permalänk
Skrivet av Yoshman:

Båda versionerna gör rent logiskt en eller två modulo-operationer.

Titta på hans ursprungliga post en gång till Den har inga modulo-operationer.

Edit: Jag tror du tittar på Mikael07s kod.

Permalänk
Datavetare
Skrivet av VirtualIntent:

Titta på hans ursprungliga post en gång till Den har inga modulo-operationer.

Edit: Jag tror du tittar på Mikael07s kod.

Helt rätt kollade på Mikael07s. Ah, kollar jag på assembler och mäter cykler i den andra versionen så är den långt mycket snabbare än en faktor 10, det tar runt 60 cykler(!). Tittar man i assemblern så inser man att kompilatorn optimerat bort det mesta då funktionen är nära nog trival och randvillkoren kända.

Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer

Permalänk
Medlem

C++ template metaprogrammering

#include <iostream> template <int n> struct Sum { enum { sum = n*( !(n%3) || !(n%5) ) + Sum<n-1>::sum }; }; template <> struct Sum<1> { enum { sum = 0 }; }; int main() { std::cout << Sum<999>::sum << std::endl; }

Dold text

Försök slå run-time hastigheten på den!

Permalänk
Datavetare
Skrivet av Mikael07:

C++ template metaprogrammering

#include <iostream> template <int n> struct Sum { enum { sum = n*( !(n%3) || !(n%5) ) + Sum<n-1>::sum }; }; template <> struct Sum<1> { enum { sum = 0 }; }; int main() { std::cout << Sum<999>::sum << std::endl; }

Dold text

Försök slå run-time hastigheten på den!

Attans, satt precis och skrev en sådan implementation men matlagningen till kidsen har trots allt högre prio...
Den tar exakt tiden av en "mov" så det blir 1 cykel och borde därför bli omöjligt att slå

Edit: klar med bestyren. Får väl köra med Clojures motsvarighet till C++ template programmering då. Det roliga med Lisp är att alla "nya" finesser som finns i de flesta språk fanns redan i Lisp som har sina rötter i 50-talet. En sådan sak är LINQ i C# som utvecklarna bakom denna finess sagt rent ut att de "tagit" från Lisp. En annan sak är template meta-programmering i C++, det går att göra motsvarande sak i Lisp med det är betydligt mycket mer kraftfullt.

Anledningen att Lisp kan göra tillsynes komplicerade saker kommer från att Lisp inte gör skillnad på data och kod. Vill man spara sitt tillstånd i ett Lisp program så är det bara att skriva ner all data på disk, för att återställa tillståndet så läser man bara in alltihop igen via R i REPL funktionen som alla Lisp:ar har (REPL = Read Evaluate Print Loop).

(ns progblemet2 (:gen-class)) (defmacro macro-sum-mul[N] (apply + (filter (fn [x] (or (zero? (mod x 3)) (zero? (mod x 5)))) (range N)))) (defn fun-sum-mul[N] (apply + (filter (fn [x] (or (zero? (mod x 3)) (zero? (mod x 5)))) (range N)))) (defn -main[] (println (time (fun-sum-mul 1000)))) (println (time (macro-sum-mul 1000)))

Resultatet från detta blir
"Elapsed time: 3.844246 msecs"
233168
"Elapsed time: 0.002391 msecs"
233168

vilket visar att macro-sum-mul blir färdig omedelbart då det likt C++ meta-programmet räknat ut resultatet som del av kompileringen.

Det riktigt coola är att macro versionen innehåller helt vanlig Clojure code...

Vad som körs i praktiken ovan är detta

(println (time (fun-sum-mul 1000)))) (println (time 233168))

Dold text
Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer

Permalänk
Medlem
Skrivet av Yoshman:

Attans, satt precis och skrev en sådan implementation men matlagningen till kidsen har trots allt högre prio...
Den tar exakt tiden av en "mov" så det blir 1 cykel och borde därför bli omöjligt att slå

Edit: klar med bestyren. Får väl köra med Clojures motsvarighet till C++ template programmering då. Det roliga med Lisp är att alla "nya" finesser som finns i de flesta språk fanns redan i Lisp som har sina rötter i 50-talet. En sådan sak är LINQ i C# som utvecklarna bakom denna finess sagt rent ut att de "tagit" från Lisp. En annan sak är template meta-programmering i C++, det går att göra motsvarande sak i Lisp med det är betydligt mycket mer kraftfullt.

Anledningen att Lisp kan göra tillsynes komplicerade saker kommer från att Lisp inte gör skillnad på data och kod. Vill man spara sitt tillstånd i ett Lisp program så är det bara att skriva ner all data på disk, för att återställa tillståndet så läser man bara in alltihop igen via R i REPL funktionen som alla Lisp:ar har (REPL = Read Evaluate Print Loop).

(ns progblemet2 (:gen-class)) (defmacro macro-sum-mul[N] (apply + (filter (fn [x] (or (zero? (mod x 3)) (zero? (mod x 5)))) (range N)))) (defn fun-sum-mul[N] (apply + (filter (fn [x] (or (zero? (mod x 3)) (zero? (mod x 5)))) (range N)))) (defn -main[] (println (time (fun-sum-mul 1000)))) (println (time (macro-sum-mul 1000)))

Resultatet från detta blir
"Elapsed time: 3.844246 msecs"
233168
"Elapsed time: 0.002391 msecs"
233168

vilket visar att macro-sum-mul blir färdig omedelbart då det likt C++ meta-programmet räknat ut resultatet som del av kompileringen.

Det riktigt coola är att macro versionen innehåller helt vanlig Clojure code...

Vad som körs i praktiken ovan är detta

(println (time (fun-sum-mul 1000)))) (println (time 233168))

Dold text

Det är riktigt ballt faktiskt

Scala ska få macro-funktionalitet i 2.10 men det ser ganska hårigt ut än så länge..

Visa signatur

Kom-pa-TI-bilitet

Permalänk
Medlem
Skrivet av Mikael07:

C++ template metaprogrammering

#include <iostream> template <int n> struct Sum { enum { sum = n*( !(n%3) || !(n%5) ) + Sum<n-1>::sum }; }; template <> struct Sum<1> { enum { sum = 0 }; }; int main() { std::cout << Sum<999>::sum << std::endl; }

Dold text

Försök slå run-time hastigheten på den!

Slog compile time istället.

#include <iostream> template<int limit, int n> struct Num { enum { num = (limit - 1) / n }; }; template<int n> struct Sum { enum { sum = n * (n + 1) / 2 }; }; template<int limit, int n> struct Res { enum { res = Sum<Num<limit,n>::num>::sum * n }; }; template<int limit, int n, int m> struct FinalRes { enum { res = Res<limit,n>::res + Res<limit,m>::res - Res<limit,n*m>::res }; }; int main(int argc, char** argv) { std::cout << FinalRes<1000,3,5>::res << std::endl; return 0; }

Dold text

Eller utan templates ifall någon har svårt att följa.

#include <iostream> int f(int limit, int n) { int num = (limit - 1) / n; int sum = num * (num + 1) / 2; return sum * n; } int main(int argc, char** argv) { std::cout << f(1000,5) + f(1000,3) - f(1000,3*5) << std::endl; return 0; }

Dold text

ps. jag är väldigt obekant med variadiska templates, vore det möjligt att göra min första lösning mer generell?

Visa signatur

"Some poor, phoneless fool is probably sitting next to a waterfall somewhere, totally unaware of how angry and scared he's supposed to be." - Duncan Trussell

Permalänk
Medlem

Jag har haft lite för kul idag..

/** * Arithmetic */ template<typename A, typename B> struct Add; template<typename A, typename B> struct Sub; template<typename A, typename B> struct Mul; template<typename A, typename B> struct Div; /** * Integer */ template<int N> struct Int { enum { value = N }; }; template<int N, int M> struct Add<Int<N>,Int<M>> { typedef Int<N+M> result; }; template<int N, int M> struct Sub<Int<N>,Int<M>> { typedef Int<N-M> result; }; template<int N, int M> struct Mul<Int<N>,Int<M>> { typedef Int<N*M> result; }; template<int N, int M> struct Div<Int<N>,Int<M>> { typedef Int<N/M> result; }; /** * List */ struct Empty; template<typename X, typename XS> struct List; template<typename YS> struct Add<Empty,YS> { typedef YS result; }; template<typename XS> struct Add<XS,Empty> { typedef XS result; }; template<typename X, typename XS, typename Y, typename YS> struct Add<List<X,XS>,List<Y,YS>> { typedef List<X,typename Add<XS,List<Y,YS>>::result> result; }; /** * Foldl */ template<template<typename,typename> class F, typename Z, typename XS> struct Foldl; template<template<typename,typename> class F, typename Z> struct Foldl<F, Z, Empty> { typedef Z result; }; template<template<typename,typename> class F, typename Z, typename X, typename XS> struct Foldl<F, Z, List<X,XS>> { typedef typename Foldl<F,typename F<Z,X>::result,XS>::result result; }; /** * Map */ template<template<typename> class F, typename XS> struct Map; template<template<typename> class F> struct Map<F, Empty> { typedef Empty result; }; template<template<typename> class F, typename X, typename XS> struct Map<F, List<X,XS>> { typedef List<typename F<X>::result,typename Map<F,XS>::result> result; }; /** * Tuples */ template<typename A, typename B> struct Tuple; template<typename T> struct MultTuple; template<typename A, typename B> struct MultTuple<Tuple<A,B>> { typedef typename Mul<A,B>::result result; }; template<typename X> struct PairWithListElem { template<typename E> struct F { typedef Tuple<X,E> result; }; }; template<typename XS> struct MakePairsFromList; template<> struct MakePairsFromList<Empty> { typedef Empty result; }; template<typename X> struct MakePairsFromList<List<X,Empty>> { typedef Empty result; }; template<typename X, typename XS> struct MakePairsFromList<List<X,XS>> { typedef typename Add< typename Map<PairWithListElem<X>::template F,XS>::result, typename MakePairsFromList<XS>::result >::result result; }; /** * Le actual problem */ template<typename L, typename N> struct Num; template<int L, int N> struct Num<Int<L>,Int<N>> { typedef typename Div< typename Sub< Int<L>, Int<1> >::result, Int<N> >::result result; }; template<typename N> struct Sum; template<int N> struct Sum<Int<N>> { typedef typename Div< typename Mul< Int<N>, typename Add< Int<N>, Int<1> >::result >::result, Int<2> >::result result; }; template<typename L, typename N> struct SumLN; template<int L, int N> struct SumLN<Int<L>,Int<N>> { typedef typename Mul< typename Sum<typename Num<Int<L>,Int<N>>::result>::result, Int<N> >::result result; }; template<typename T> struct MapSumLN; template<int L> struct MapSumLN<Int<L>> { template<typename N> struct F; template<int N> struct F<Int<N>> { typedef typename SumLN<Int<L>,Int<N>>::result result; }; }; #include <iostream> int main(int argc, char** argv) { // input #define LIMIT Int<1000> #define LIST List<Int<3>,List<Int<5>,Empty>> // add up sums #define MAPPED Map<MapSumLN<LIMIT>::F,LIST>::result #define FOLDED Foldl<Add,Int<0>,MAPPED>::result // subtract duplicates #define PAIRS MakePairsFromList<LIST>::result #define PAIRS_MAPPED Map<MultTuple,PAIRS>::result #define SUB_LIST Map<MapSumLN<LIMIT>::F,PAIRS_MAPPED>::result #define SUB Foldl<Add,Int<0>,SUB_LIST>::result #define RESULT Sub<FOLDED,SUB>::result std::cout << RESULT::value << std::endl; return 0; }

Dold text

Mer generell lösning i compile time kommer jag nog inte på, wiki-entry'n för variadic templates gav mig huvudverk.

Visa signatur

"Some poor, phoneless fool is probably sitting next to a waterfall somewhere, totally unaware of how angry and scared he's supposed to be." - Duncan Trussell

Permalänk
Datavetare

Ruby

Lösningen i Ruby försvann lite i allt virrar ovan, här är en än kortare version i detta utmärkta skriptspråk

1000.times.select{ |n| n%3==0||n%5==0 }.reduce(&:+)

Det man lär inse att ALLT är objekt i Ruby.
1000.times betyder att man anropar metoden times på objektet 1000 som är en instans av klassen Integer
Resultatet är en ström av heltal från 0 till 999 och denna ström är en instans av klassen Enumerable
Vart och ett av dessa heltal stoppas in i blocket {|n|n%3==0||n%5==0 } som i praktiken betyder: anonym funktion men en formell parameter som här kallas n. Funktionens returnvärde är resultatet av jämförelsen n%3==0||n%5==0. Resultatet av select är en ny ström med de tal där blocket evaluerade till true.
Denna nya ström skickas till reduce(&:+) som man kan läsa som: rada upp alla tal och stoppa in funktionen med namnet + mellan dessa. Kolon är lite som att ta adressen av funktionen efter, så om en funktion heter foo så skulle man kunna skriva bar(:foo) om bar tar en funktion som argument.

Dold text

Faktiskt rätt lättläst trots att det väldigt kompakt, men i "riktig" kod fördrar jag ändå att skriva saker lite mer explicit då det gör det lättare för andra att läsa-

Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer