tags:

views:

1213

answers:

7

I'm currently learning F# quite intensively. I really love it as a language, it just sort of 'feels' right and seems to allow you to produce some succint elegant code.

I'm interested in finding some really nice 'wow factor' snippets of F# code which demonstate the elegence of the language, especially compared to C#. For example I really like:-

#light
let ListProduct l = List.fold_left (*) 1 l

Which inputs a list of ints and multiplies each element in the list, i.e. obtains the product of the list (e.g. a list of 1,2,3 would be calculated as 1*2*3=6). The closest C# equivilent, using LINQ and functional concepts as as follows:-

using System;
using System.Collections.Generic;
using System.Linq;

...

public static class ListHelper {
  public static int ListProduct(List<int> l) {
    return l.Aggregate(1, (i, j) => i * j);
  }
}

Before LINQ that would have been:-

using System;
using System.Collections.Generic;

...

public static class ListHelper {
  public static int ListProduct(List<int> l) {
    int ret = 1;
    foreach (int i in l) ret *= i;
    return ret;
  }
}

I'm certainly not trying to criticise C# here, I think it's a wonderful language, it's just nice to see how F# compares and to see how it can do some things more elegantly - does anyone have anything really nice?

+2  A: 

F# is a functional programming language and therefore is great with lists and recursion.

The code below, is a slight modification to part of the default tutorial F# Project included with the F# download package. It is nothing special but demonstrates the same code as you put above for those that are wondering.

let rec ListProduct xs =
    match xs with
    //If xs is an empty list, we have a match with an empty list.  Return 1
    | []    -> 1
    //Otherwise match with an item + the rest of the list.  
    //Return the first item * the rest of the list. 
    | y::ys -> y * ListProduct ys

This code is obviously not meant to give any wow factor as you mentioned. But you can see some really cool uses of F# on this site. Check out the sudoku solver in F#. Compare this code to a C# implementation of a Sudoku solver. The site also demonstrates how to easily code a GUI with F#.

This site will show you how to integrate F# with ASP .Net

Brian R. Bondy
+15  A: 

My favorite is recursively listing all files under a folder in a four-line sequence expression:

open System.IO

let rec filesUnderFolder basePath =
    seq {
        for file in Directory.GetFiles(basePath) do
            yield file
        for subDir in Directory.GetDirectories(basePath) do
            yield! filesUnderFolder subDir
        }
Chris Smith
Albeit cute there's actually a straigforward way to achive this using"Directory.GetFiles(basePath, "*", SearchOption.AllDirectories)"see http://msdn.microsoft.com/en-us/library/ms143316.aspx
Torbjörn Gyllebring
First comment is NOT true - the function that is proposed in this answer is lazy - so it won't oblige you to scan the whole of your basePath before returning the first result.
Benjol
+1  A: 
let htmls = [ "<crap>foo</crap>"; "<crap>bar</crap>"]

let remove pat (x:string) = x.Replace(pat,"") 

let removecrap = [ remove "<crap>"; remove "</crap>" ]

removecrap is a list of functions that take a string and return a string

let rec mapf (lf: ('a->'a) list) (li: 'a list) = 
   match lf with
   | [] -> li
   | hd::tl -> mapf tl (List.map hd li)

let result = mapf removecrap htmls

Instead of List.map applying one function to every element in a list, mapf now applies a list of functions to every element in a list. Personally I like this construction a lot, but I wonder if there is a more standard F# way of doing this.

Michiel Borkent
Perhaps, a more standard F# way is function composition (>>). This way, you just map once.let removecrap = remove "<crap>" >> remove "</crap>"let result = map removecrap htmls
namin
+4  A: 

For F# elegance, check out Dustin's YAPES series: http://diditwith.net/2008/04/24/YetAnotherProjectEulerSeriesYAPES.aspx

Brian
+3  A: 

I think the elegant thing about folding is that you can 'feed' it anything:

//takes a max/min tuple + new value, returns expanded max/min tuple
let limits (mn, mx) a = (min mn a, max mx a)

//Initialise a test list
let lst = [1; 3; 5; -1; -9; 0]

//feeds each value in lst to limits - for first call, uses (0, 0) for (mn,mx)
List.fold_left limits (0, 0) lst

//(two extra functions for following example)
let cube x = x * x * x    //does this need explaining?
let range (a, b) = b - a  //returns range of a tuple

//particularly sexy with pipe forward operator
lst |> List.map cube
    |> List.fold_left limits (0, 0)
    |> range
Benjol
+3  A: 

Despite the prevalent negativity surrounding Fibonacci examples. I rather like them. It turns out that "practical" programming is often a composition of many "impractical"-looking things. The Fibonacci example shows us what a doubly recurrent declaration can look like. Haskell has a particularly elegant solution that can teach us something about F#:

fibonacci = 0 : 1 : zipWith (+) fibonacci (tail fibonacci)

First of all, study this snippet and get it. fibs defines itself in terms of itself lazily. We can get some lazy out of F# using sequences. Using FSI, here's how:


> let fibonacci = Seq.unfold (fun (x, y) -> Some(x, (y, x + y))) (0I,1I);;

val fibonacci : seq


> #time;;

--> Timing now on


> fibonacci |> Seq.nth 10000;;

Real: 00:00:00.027, CPU: 00:00:00.031, GC gen0: 0, gen1: 0, gen2: 0

val it : System.Numerics.BigInteger =

33644764876431783266621612005107543310302148460680063906564769974680081442166662368155595513633734025582065332680836159373734790483865268263040892463056431887354544369559827491606602099884183933864652731300088830269235673613135117579297437854413752130520504347701602264758318906527890855154366159582987279682987510631200575428783453215515103870818298969791613127856265033195487140214287532698187962046936097879900350962302291026368131493195275630227837628441540360584402572114334961180023091208287046088923962328835461505776583271252546093591128203925285393434620904245248929403901706233888991085841065183173360437470737908552631764325733993712871937587746897479926305837065742830161637408969178426378624212835258112820516370298089332099905707920064367426202389783111470054074998459250360633560933883831923386783056136435351892133279732908133732642652633989763922723407882928177953580570993691049175470808931841056146322338217465637321248226383092103297701648054726243842374862411453093812206564914032751086643394517512161526545361333111314042436854805106765843493523836959653428071768775328348234345557366719731392746273629108210679280784718035329131176778924659089938635459327894523777674406192240337638674004021330343297496902028328145933418826817683893072003634795623117103101291953169794607632737589253530772552375943788434504067715555779056450443016640119462580972216729758615026968443146952034614932291105970676243268515992834709891284706740862008587135016260312071903172086094081298321581077282076353186624611278245537208532365305775956430072517744315051539600905168603220349163222640885248852433158051534849622434848299380905070483482449327453732624567755879089187190803662058009594743150052402532709746995318770724376825907419939632265984147498193609285223945039707165443156421328157688908058783183404917434556270520223564846495196112460268313970975069382648706613264507665074611512677522748621598642530711298441182622661057163515069260029861704945425047491378115154139941550671256271197133252763631939606902895650288268608362241082050562430701794976171121233066073310059947366875

{IsEven = false; IsOne = false; IsPowerOfTwo = false; IsZero = false; Sign = 1;}


That's nice. Even for my modest workstation. It's practical because we've defined something elegantly in one line that outperforms naive multi-line implementations. We've harnessed some practical concepts while we're at it. Currying (as demonstrated with the pipeline to Seq.nth), lazy evaluation (as demonstrated by Seq.unfold), and lambdas (the anonymous function given to Seq.unfold). We've also taken advantage of the "shape" of the Fibonacci sequence computation in not quite the same way as the Haskell version, but similar enough.

thinkhard