2009-05-04 12 views

Répondre

9

I'D utiliser quelque chose comme ce qui suit:

smaller :: String -> String -> Bool 
smaller s1 s2 | len1 /= len2   = (len1 < len2) 
       | otherwise   = (s1 < s2) 
       where (len1, len2) = (length s1, length s2) 

Voici un échantillon analysé, dans étreintes:

 
Main> smaller "b" "aa" 
True 
Main> smaller "aa" "b" 
False 
Main> smaller "this" "that" 
False 
Main> smaller "that" "this" 
True 
+0

Ceci est évidemment plus rapide que ma version, car il calcule une seule fois la longueur des chaînes. Il peut probablement être rendu encore plus rapide en effectuant une correspondance directe de modèle sur les chaînes d'entrée, fusionnant ainsi cette définition et la définition de la fonction 'length'. –

+1

Haskell est axé sur la généralisation et les bonnes pratiques de codage, et il a un très bon système de type (classe). Pourquoi ne pas réécrire la fonction comme 'compare ':: Ord a => [a] -> [a] -> Ordering ou quoi? –

+1

@Aleksander: Cela dépend de ce que veut le PO.Puisque le PO a posé une question assez élémentaire, peut-être veut-il une réponse élémentaire? Je ne doute pas qu'il existe des manières plus rapides et/ou plus idiomatiques d'écrire une telle fonction dans Haskell, mais peut-être est-il préférable de garder les choses simples pour aider le PO à apprendre. –

5

Essayez ceci:

compare s1 s2 

(Ce retour LT, EQ ou GT).

+0

Je pense que l'OP veut que les chaînes plus courtes soient «plus petites» que les chaînes plus longues, quel que soit leur contenu. comparez "b" "aa" renvoie GT, mais je pense que l'OP voudrait une fonction qui renvoie LT dans ce cas. –

2

String est une instance de Ord et vous pouvez donc utiliser toutes ces méthodes pour comparer une chaîne lexicographiquement. Comme Andrew a dit, c'est essentiellement compare mais aussi les opérateurs de comparaison, (<) entre autres.

smaller :: Ord a => a -> a -> Bool 
smaller a b = a < b 

Cela fonctionne pour tous les types la mise en œuvre Ord (et est vraiment juste un emballage brut pour (<)), y compris String.

+0

Je pense que l'OP veut que les chaînes plus courtes soient «plus petites» que les chaînes plus longues, quel que soit leur contenu. Par exemple, le plus petit "b" "aa" devrait renvoyer True. (<) ne prend pas en compte les longueurs, donc je ne crois pas que votre réponse soit tout à fait ce que le PO demande. –

2

La comparaison de chaînes normales ne fonctionne que sur l'ordre lexicographique et non sur la longueur des chaînes.

Vous devriez écrire votre propre fonction de vérifier également la longueur:

smaller :: String -> String -> Bool 
smaller s1 s2 | length s1 < length s2 = True 
       | length s1 > length s2 = False 
       | otherwise    = s1 < s2 

Ou un peu plus générale:

compareStrings :: String -> String -> Ordering 
compareStrings s1 s2 | length s1 < length s2 = LT 
        | length s1 > length s2 = GT 
        | otherwise    = compare s1 s2 

Exemple:

ghci> compare "ab" "z" 
LT 
ghci> compareStrings "ab" "z" 
GT 

Nous étions en train de jouer avec les monoïdes à université la semaine dernière, et nous sommes arrivés avec cette belle solution de rechange Ord exemple:

instance Ord a => Ord [a] where 
    compare = comparing length 
       `mappend` comparing head `mappend` comparing tail 

Mais si vous ne comprenez pas tout à fait cela, je vous suggère de tenir à la première définition ;-)

+2

Une partie de la raison pour laquelle comparer les longueurs est utile est que de nombreuses implémentations de String dans d'autres langues stockent ou mettent en cache la longueur de la chaîne. L'avantage est que la plupart des comparaisons de chaînes prendront alors le temps O (1) en fonction de la longueur des chaînes. Ce n'est pas le cas chez Haskell. Toutes les comparaisons de chaînes avec l'implémentation native prendront au moins O (min (m, n)) en fonction des longueurs des chaînes. – yfeldblum

+2

Bien sûr, et ce n'est même pas l'implémentation la plus rapide possible. C'est juste que je pensais que le questionneur a demandé une version de comparaison où les cordes ont d'abord été vérifiées pour la longueur avant l'ordre lexicographique. Cela peut être utile si vous souhaitez imprimer une liste de chaînes et pensez qu'il est plus simple d'imprimer des chaînes plus petites en premier. –

+0

@Tom: Je ne suis pas sûr que votre petite fonction fonctionne correctement si s2 est plus long que s1. Par exemple, "aa" "b" plus petit renvoie True. Il me semble que l'OP veut que la fonction retourne False dans ce cas. –

7

La solution one-pass:

lengthcompare :: Ord a => [a] -> [a] -> Ordering 
lengthcompare = lc EQ 
where 
    lc lx [] [] = lx 
    lc _ [] _ = LT 
    lc _ _ [] = GT 
    lc EQ (v:vs) (w:ws) = lc (compare v w) vs ws 
    lc lx (_:vs) (_:ws) = lc lx vs ws 

smaller :: Ord a => [a] -> [a] -> Bool 
smaller s1 s2 = lengthcompare s1 s2 == LT 
4

Une version plus courte de la version mappend par Tom Lokhorst ci-dessus:

import Data.Monoid (mappend) 
import Data.Ord (comparing) 

compareStrings :: String -> String -> Ordering 
compareStrings = comparing length `mappend` comparing id 

Une autre façon, en tirant parti de l'ordre de tuples:

import Data.Ord (comparing) 
import Control.Arrow ((&&&)) 

compareStrings :: String -> String -> Ordering 
compareStrings = comparing (length &&& id) 
+0

Ah oui, je pensais à une définition alternative, mais en définissant une nouvelle fonction, vous pouvez évidemment utiliser l'ordre existant sur les listes. Belles définitions! –

+0

En outre, j'ai vraiment besoin de commencer à apprendre la bibliothèque de flèches. Je vois continuellement des gens arriver avec ces belles définitions élégantes, et c'est juste en utilisant les instances de la fonction ... –

+0

Oui, ceux-ci sont gentils et courts. comparaison id == comparer, donc voici une autre alternative: comparaison longueur 'mappend' comparer – Martijn