2009-05-04 6 views

risposta

9

mi piacerebbe usare qualcosa come il seguente:

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

Ecco un esempio di esecuzione, in Abbracci:

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

Questo è ovviamente più veloce della mia versione, dal momento che calcola solo la lunghezza delle stringhe una volta. Probabilmente può essere reso ancora più veloce eseguendo una corrispondenza di pattern diretta sulle stringhe di input, unendo così questa definizione e la definizione della funzione 'length'. –

+1

Haskell si occupa di generalizzazione e buone pratiche di codifica e ha un sistema di tipo (classe) molto buono. Perché non riscrivere la funzione come 'compare ':: Ord a => [a] -> [a] -> Ordinazione' o giù di lì? –

+1

@Aleksander: Dipende da ciò che l'OP vuole.Dal momento che l'OP ha posto una domanda abbastanza elementare, forse lui/lei vuole una risposta elementare? Non dubito che ci siano modi più veloci e/o più idiomatici per scrivere una funzione simile in Haskell, ma forse è meglio mantenere le cose semplici per aiutare l'OP a imparare. –

5

Prova questo:

compare s1 s2 

(Restituisce LT, EQ, o GT).

+0

Penso che l'OP voglia stringhe più brevi per essere "più piccole" di stringhe più lunghe indipendentemente dal loro contenuto. confronta "b" "aa" restituisce GT, ma penso che l'OP vorrebbe una funzione che restituisca LT in questo caso. –

2

String è un'istanza di Ord e pertanto è possibile utilizzare tutti questi metodi per confrontare lessicograficamente la stringa. Come ha detto Andrew, questo è essenzialmente compare ma anche gli operatori di confronto, (<) tra gli altri.

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

Questo funziona per tutti i tipi di attuazione Ord (ed è in realtà solo un wrapper greggio per (<)), tra cui String.

+0

Penso che l'OP vuole che le stringhe più corte siano "più piccole" delle stringhe più lunghe indipendentemente dal loro contenuto. Ad esempio, "b" più piccola "aa" dovrebbe restituire True. (<) non tiene conto della lunghezza, quindi non credo che la tua risposta sia esattamente ciò che l'OP sta chiedendo. –

2

La stringa di confronto normale funziona solo sull'ordinamento lessicografico, non sulla lunghezza delle stringhe.

Quindi dovreste scrivere la propria funzione di controllare anche per la lunghezza:

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

o un po 'più generale:

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

Esempio:

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

Eravamo in giro con i Monoidi a università la settimana scorsa, e ci si avvicinò con questa bella alternativa Ord esempio:

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

Ma se non capisco questo, vi consiglio di bastone con la prima definizione ;-)

+2

Parte del motivo per confrontare le lunghezze è utile se molte implementazioni di String in altri linguaggi memorizzano o memorizzano nella cache la lunghezza della stringa. Il vantaggio è che la maggior parte dei confronti tra stringhe richiederà quindi il tempo O (1) in funzione delle lunghezze delle stringhe. Questo non è il caso di Haskell. Tutti i confronti di stringhe con l'implementazione nativa richiederanno almeno O (min (m, n)) in funzione delle lunghezze delle stringhe. – yfeldblum

+2

Certo, e questa non è nemmeno l'implementazione più veloce possibile. È solo che pensavo che l'interlocutore chiedesse una versione di compare dove le stringhe venivano prima controllate per la lunghezza prima dell'ordine lessicografico. Questo potrebbe essere utile se si desidera stampare un elenco di stringhe e ritenerlo più bello stampare prima stringhe più piccole. –

+0

@ Tom: Non sono sicuro che la tua funzione più piccola funzioni correttamente se s2 è più lungo di s1. Ad esempio, "aa" "b" più piccolo restituisce True. Mi sembra che l'OP voglia che la funzione restituisca False in questo caso. –

7

La soluzione di un passaggio:

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

Una versione più corta della versione mappend di Tom Lokhorst sopra:

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

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

altro modo, sfruttando l'ordinamento delle tuple:

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

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

Oh, giusto, stavo pensando ad una definizione alternativa, ma quando si definisce una nuova funzione, è ovviamente possibile utilizzare l'ordinamento esistente negli elenchi. Belle definizioni! –

+0

Inoltre, ho davvero bisogno di iniziare ad apprendere la libreria di Arrows. Vedo continuamente persone che escono con queste belle definizioni eleganti, e questo è solo usando le istanze di funzione ... –

+0

Sì, queste sono belle e brevi. confronto ID == confronta, quindi ecco un'altra alternativa: confronto lunghezza 'mappend' confrontare – Martijn

Problemi correlati