2010-06-10 6 views
17

Je ne peux pas trouver "faire ... alors ...""do ... while" n'existe pas dans F #

Je dois code comme ceci:

let bubbleSort a= 
    let n = Array.length a 
    let mutable swapped = true 
    let mutable i = 0 
    while swapped do 
     swapped <- false 
     for j = 0 to n-i-2 do 
      if a.[j] > a.[j+1] then 
       let t = a.[j] 
       a.[j] <- a.[j+1] 
       a.[j+1] <- t 
       swapped <- true 
     i <- i+1 

Le code est mauvais sans "faire ... tandis que".
Malheureusement, "break/continue" ne sont pas disponibles.

+0

Vous publiez du code spécifique mais la question "Est-ce que F # ne convient pas pour la programmation non-fonctionnelle?" est assez large et vague. Baser votre impression d'une langue sur un cas d'utilisation me semble être une sorte de précipitation vers le jugement. –

+0

Un code spécifique mal écrit pour un problème de jouet ... –

+3

Bien que je sois d'accord avec le fait que Kev rende la réclamation un peu trop large, j'ai partagé la même frustration sur le manque de break/continue lors d'un concours auquel j'assistais il y a un mois. J'admets que je suis encore relativement nouveau à F #. – Cygwin98

Répondre

25

F # est très approprié pour la programmation non fonctionnelle. En fait, être capable d'affiner des parties d'un algorithme dans un style impératif est l'un des principaux points forts du langage pour moi. Par exemple, en abordant un problème de projet Euler, j'ai commencé avec une solution fonctionnelle propre en utilisant des ensembles et des plis immuables. Il a fallu 150 secondes pour terminer. Le fait d'avoir mis en place le cadre de mon algorithme m'a permis de séparer les structures de données et de plier les opérations une à la fois jusqu'à ce que je parvienne à réduire le temps d'exécution à 5 secondes. Ma dernière solution était très impérative (et même légèrement plus rapide qu'une version C# équivalente).

Comme vous pouvez le voir, je l'ai résolu en codant d'abord une solution de style fonctionnel, puis en réécrivant les petites pièces à un style impératif. Ne pas avoir à traiter avec des indices et d'autres conditions de boucle explicitement gardé le code plus compréhensible pour moi. Une fois que vous apprendrez à penser comme un programmeur fonctionnel, vous constaterez que vous aurez rarement besoin de pauses et continue. C'est ce que j'ai vécu. Mais si vous en avez besoin, savoir comment penser d'une manière fonctionnelle aide à trouver des solutions de rechange, impliquant généralement une version récursive de ce qui était une boucle.Au moment où vous commencerez à penser plus de manière F # idiomatique, vous verrez probablement de plus en plus de code récursif (tail-) remplaçant ce que vous faisiez avec les constructions en boucle. Heck, écrit F # depuis 2 ans maintenant a déformé mon esprit jusqu'à présent que je suis plus susceptible de choisir la récursivité et se couche sur les boucles.

Chaque fois que je pense que j'ai besoin d'interrompre/continuer, je ne le fais généralement pas parce qu'il y a une version plus propre de l'algorithme caché et qui attend de sortir. Le plus grand défi est d'apprendre à trouver cette version plus propre. J'ai peur que beaucoup de pratique et de bons exemples soient la seule façon de mieux penser fonctionnellement, mais je crois que c'est un effort bien dépensé. Ironiquement, le tri à bulles est un algorithme qui est en fait conçu pour les tableaux avec des contenus mutables. Tout type de bulle récursif est susceptible d'être plus difficile à comprendre qu'une version impérative. Je pense que je viens de tuer mon propre poste ici.

+0

Pas tout à fait pertinent, mais 150 secondes à 5 secondes semble être un grand saut. J'aimerais vraiment le voir moi-même, y at-il une chance que vous puissiez poster les versions impératives et fonctionnelles de votre programme? – Juliet

+2

@Juliet: Je veux honorer la demande du projet Euler de ne pas poster de solutions (pour les problèmes les plus élevés au moins). Mais je peux vous dire comment j'ai obtenu une telle accélération: j'ai remplacé itération sur les expressions de séquence avec des boucles. J'ai remplacé BigRational par ma propre classe de fractions qui correspond à un nombre entier. Et l'accélération la plus importante: J'ai remplacé Set <_> par le mutable HashSet <_>. Ainsi, l'accélération n'était pas vraiment due au changement de style fonctionnel -> impératif, mais à la sélection de structures de données plus appropriées pour mon problème (recherche O (1) par rapport à O (n log n)). – cfern

+0

Vous voulez dire O (log n) recherche. :-) –

9

break et continue serait un ajout de fonctionnalité très utile; ce sont des mots réservés, et peut-être que nous les verrons dans une future version de la langue. Le manque d'eux est un ennui mineur occasionnel, mais rend à peine la langue «inadaptée». En attendant, une sentinelle mutable fonctionne, comme vous l'avez vu dans votre exemple.

Voir aussi

http://tomasp.net/blog/imperative-ii-break.aspx/

+3

IMHO la raison break & continuer n'existe pas dans F # est parce que le langage de base est essentiellement OCaml. Le compilateur OCaml produit une gestion d'exception TRES efficace qui est tout à fait appropriée pour le contrôle de flux, en raison d'un surcoût extrêmement faible.D'un autre côté, la plate-forme .NET a une gestion des exceptions beaucoup moins efficace, ce qui est totalement inadapté au flux de contrôle. –

+0

Aussi, tout à fait d'accord avec Brian ... maintenant que j'ai un bon contrôle sur F #, ces constructions sont rarement manqués; son absence ne constitue qu'un désagrément mineur. Mais, ils seraient utiles parfois, en particulier pour les nouveaux venus et lors du portage d'algorithmes écrits dans d'autres langages impératifs. –

+0

Brian, merci pour le lien. C'est un petit bijou. – Cygwin98

4

Je ne sais pas F # très bien, mais F # est un langage fonctionnel. Habituellement, il n'y a pas de boucle "for" ou "while" dans les langages de programmation fonctionnels.

Les langages fonctionnels définissent des fonctions dans un sens mathématique (comme f (x) => ...). Écrire un programme revient à définir et combiner un ensemble de fonctions mathématiques. Cela signifie que la seule façon de coder les boucles est d'utiliser la récursivité.

En mathématiques, il n'y a aucun moyen de dire:

f(x) => "do 5 times this" 

Qu'est-ce que vous feriez est de définir f comme:

    count > 0 : f(x, count-1) 
f(x, count) => { 
       count <= 0 : ... 

Et puis utilisez cette fonction comme dans:

y = f(x, 5) 

Cela correspond exactement à la façon dont vous implémentez les fonctions dans les langages fonctionnels. Au moins, cela est vrai pour les langages purement fonctionnels comme Haskell ...

+0

Il en est de même pour le schéma – Thea

+1

Oui, c'est vrai. Je déteste les gens downvoting réponses sans même donner une raison pourquoi ... –

5

Bien qu'un peu plus bavard, vous pouvez utiliser les fonctions récursives pour éviter le « faire tout » comme dans:

let swap (a:int[]) i j = 
    let t = a.[i] 
    a.[i] <- a.[j] 
    a.[j] <- t 

let rec bubbleSortAux a nMax j swapped = 
    if j >= 0 && j <= nMax then 
    if a.[j] > a.[j+1] then 
     swap a j (j+1) 
     bubbleSortAux a nMax (j+1) true 
    else 
     bubbleSortAux a nMax (j+1) false 
    else 
    swapped 

let rec bubbleSortLoop a nMax = 
    if bubbleSortAux a nMax 0 false then 
    bubbleSortLoop a (nMax - 1) 

let bubbleSort a = 
    bubbleSortLoop a (a.Length - 2) 
4
let bubbleSort (a: _ []) = 
    let mutable fin = false 
    while not fin do 
    fin <- true 
    for i=0 to a.Length-2 do 
     if a.[i] > a.[i+1] then 
     let t = a.[i] 
     a.[i] <- a.[i+1] 
     a.[i+1] <- t 
     fin <- false 
3

do/while n'est pas disponible car F # est un langage fonctionnel et ce genre de construction est spécifique aux langages impératifs. Pause/continuation n'est pas non plus disponible pour les mêmes raisons. Cependant, vous pouvez toujours écrire do/while en F #. Les blocs de code suivantes sont équivalentes:

en C#

do 
{ 
    System.Console.WriteLine("processing something..."); 
    System.Console.WriteLine("doing something complicated"); 

    System.Console.Write("continue?"); 
} while (Console.ReadLine() == "y"); 

en F #

let doSomethingAndContinue() = 
    printfn "processing something..." 
    printfn "doing something complicated" 
    printf "continue?" 
    System.Console.ReadLine()="y" 

while doSomethingAndContinue() do ignore None 
+0

Assez bon et très utile. Les parenthèses sur doSomethingAndContinue() sont-elles considérées comme du sucre syntaxique? – octopusgrabbus

+0

Non; L'application function nécessite un argument, donc les fonctions prenant des arguments "no" doivent être passées à la valeur unitaire,() –

4

Il se révèle être assez facile d'écrire un assez bon do-while en F # en-supérieur fonction de commande:

let doWhile f c = 
    f() 
    while c() do 
     f() 
Questions connexes