Teraz używam takiej metody:Czy istnieje prosty lub piękny sposób na odwrócenie ciągu znaków?
let x_rev = new string(x.Reverse().ToArray())
Teraz używam takiej metody:Czy istnieje prosty lub piękny sposób na odwrócenie ciągu znaków?
let x_rev = new string(x.Reverse().ToArray())
Oto kod oparty na komentarzu Timwi'ego do odpowiedzi Nate'a. Istnieją pojedyncze litery logiczne (wyświetlane na ekranie), które składają się z więcej niż jednego rzeczywistego znaku. Odwracanie kolejności znaków zamienia te litery w bełkot.
Timwi wskazuje, że framework zapewnia TextElementEnumerator, który działa w kategoriach logicznych elementów tekstowych, a nie znaków i obsługuje poprawnie te litery. Nie słyszałem o tej klasie wcześniej, więc napisałem kod, który używa TextElementEnumerator, aby poprawnie odwrócić ciąg znaków i porównać wyniki z naiwnym odwróceniem łańcucha znaków.
open System
open System.Globalization
// five characters that render as three letters: "àÆ"
let myString = "\uD800\uDC00\u0061\u0300\u00C6"
// naive reversal scrambles the string: "Æ̀a��"
// (note that while this produces the wrong results,
// it probably is faster on large strings than using
// LINQ extension methods, which are also wrong)
let naive = String(myString.ToCharArray() |> Array.rev)
// use a TextElementEnumerator to create a seq<string>
// that handles multi-character text elements properly
let elements =
seq {
let tee = StringInfo.GetTextElementEnumerator(myString)
while tee.MoveNext() do
yield tee.GetTextElement()
}
// properly reversed: "Æà"
let reversed = elements |> Array.ofSeq |> Array.rev |> String.concat ""
dzięki za drugą metodę ... jej najpiękniejszy i najprostszy sposób myślę: niech naive = String (myString.ToCharArray() |> Array.rev) – AndrewShmig
sprawdź moje najnowsze rozwiązanie, znalazłem inny sposób uniknięcia ' TextElementEnumerator' który tym razem nie powoduje trafienia wydajności. –
Jeśli to, co robisz jest z MSDN na Enumerable.Reverse() to prawdopodobnie dostał najprostsze rozwiązanie.
Jeśli nie korzystasz z .NET 3.5 (przeczytaj LINQ (nie wiesz, czy przedtem F # było wcześniej), możesz użyć metody Array.Reverse(), ale wynikowy kod jest bardzo podobny.
Wystarczy powiedzieć, że masz najbardziej elegancki sposób na odwrócenie łańcucha, wielokrotnie używałam Enumerable.Reverse()
, aby odwrócić kolejność ciągów w moich projektach. Oczywiście, jeśli konstruktor String przyjąłby IEnumerable<Char>
, możemy pominąć bit .ToArray()
, który moim zdaniem sprawiłby, że kod byłby nieco lepszy, ale w jego stanie, dodatkowy .ToArray()
nie jest wcale taki zły.
Jeśli naprawdę chciał, można napisać metodę rozszerzenia w C# i dodać odwołanie do tej biblioteki w # projekcie F, że metoda C# przedłużenie będzie wyglądać mniej więcej tak:
public static String ReverseString(this String toReverse)
{
return new String(toReverse.Reverse().ToArray());
}
który dodaje dodatkowe Zależność, która jest prawdziwą korzyścią, sprawia, że twój kod F # staje się nieco prostszy, jeśli odwracasz ciągi w całym miejscu, to może być tego warte, w przeciwnym razie po prostu podsumowałbym to, co masz w normalnej metodzie F # i używać go w ten sposób.
Chociaż ktoś o wiele mądrzejszy ode mnie może mieć piękniejszy sposób na zrobienie tego.
Zwróć uwagę, że jest to rzeczywiście subtelnie błędne: ciągi znaków w .NET są kodowane w UTF-16, co oznacza, że jeśli masz jakieś sekwencje specjalne (znane jako pary zastępcze), Twój odwrócony ciąg znaków będzie niepoprawny. –
@romkyns To prawda, ale w typowych przypadkach jest to wystarczająco dobre, pod warunkiem, że wiesz, że nie będzie działać, gdy napotkasz zastępcze pary lub znaki, których nie można przedstawić za pomocą jednego bajtu/znaku. – Nate
To zależy, tak. To dlatego, że ASCII zostało uznane za "wystarczająco dobre", że wiele osób spoza USA nadal cierpi z powodu słabego wsparcia językowego. Z UTF-16 jest gorzej, ponieważ tym razem większość 1. świata jest w porządku, a programy łamią się tylko w mało znanych krajach. Więc tak, jest wystarczająco dobre dla wewnętrznej aplikacji, ale nie odcina jej na coś takiego, jak odtwarzacz multimedialny o otwartym kodzie źródłowym. –
Moja odpowiedź opiera się na odpowiedzi @ Joela, która z kolei została oparta na odpowiedzi @ Timwi. Przedstawię ją za najpiękniejszą i prosty prawidłowego odpowiedź, choć nie na pewno najlepszym wykonaniu (fałda użyciu +
zabija to, ale głównym poprawa beautifying korzysta ParseCombiningCharacters
i GetNextTextElement
zamiast tego rozsądku testowania TextElementEnumerator
i dodanie Reverse
jako. rozszerzenie String
jest zbyt miły):
open System
open System.Globalization
type String with
member self.Reverse() =
StringInfo.ParseCombiningCharacters(self)
|> Seq.map (fun i -> StringInfo.GetNextTextElement(self, i))
|> Seq.fold (fun acc s -> s + acc) ""
Zastosowanie:
> "\uD800\uDC00\u0061\u0300\u00C6".Reverse();;
val it : string = "Æà"
Edit:
Wymyśliłem tę nowatorską odmianę również w samochodzie dojazdowym, która prawdopodobnie działa lepiej, ponieważ używamy String.concat
.Rozszerzenie typ zostanie pominięty:
let rev str =
StringInfo.ParseCombiningCharacters(str)
|> Array.rev
|> Seq.map (fun i -> StringInfo.GetNextTextElement(str, i))
|> String.concat ""
Edit (najlepsze rozwiązanie do tej pory):
Rozwiązanie to wykorzystuje kolejną metodę StringInfo
za wyliczanie elementów tekstowych, które znów unika się stosowania nieprzyjemne pracować TextElementEnumerator
ale nie robi spowoduje dwa razy więcej połączeń z wewnętrznym StringInfo.GetCurrentTextElementLen
, podobnie jak poprzednie rozwiązanie. W tym czasie korzystam również z odwrócenia tablicy w miejscu, co powoduje znaczną poprawę wydajności.
let rev str =
let si = StringInfo(str)
let teArr = Array.init si.LengthInTextElements (fun i -> si.SubstringByTextElements(i,1))
Array.Reverse(teArr) //in-place reversal better performance than Array.rev
String.Join("", teArr)
Powyższe rozwiązanie jest w zasadzie równoważne następujące (które przerabia się w nadziei, że możemy pisk się nieco większą wydajność, ale mogę zmierzyć żadnej znaczącej różnicy):
let rev str =
let ccIndices = StringInfo.ParseCombiningCharacters(str)
let teArr =
Array.init
ccIndices.Length
(fun i ->
if i = ccIndices.Length-1 then
str.Substring(i)
else
let startIndex = ccIndices.[i]
str.Substring(startIndex, ccIndices.[i+1] - startIndex))
Array.Reverse(teArr) //in-place reversal better performance than Array.rev
String.Join("", teArr)
Twoja druga wersja jest bardzo sprytna! Moje pobieżne testy nie wykazują znaczącej różnicy w wydajności między tą wersją a wersją, która używa 'TextElementEnumerator' z wyrażeniem sekwencji. Myślę, że wolę twoje podejście. –
Spojrzałem z reflektorem i wierzę, że twoje podejście kończy się wywoływaniem metody wewnętrznej 'StringInfo.GetCurrentTextElementLen' dwa razy tyle razy, co' TextElementEnumerator'. Jeśli wydajność jest krytyczna, a łańcuchy są duże, pewna zmiana za pomocą modułu wyliczającego prawdopodobnie będzie najlepsza. –
Witam @Joel, dzięki za to, że to odzwierciedlałem, byłam ciekawa, jak powtarzające się połączenia z 'GetNextTextElement' powinny być porównywane z użyciem' TextElementEnumerator'. Równie dobrze dla 'TextElementEnumerator' jest lepsze działanie, tworzenie elementu rozszerzenia' String', być może właściwość getter o nazwie 'TextElements', która zwraca sekwencję podobną do implementacji' element', z którą najłatwiej będzie pracować na dłuższą metę. –
Łącząc najlepsze z poprzednich odpowiedzi, z niewielką aktualizacją:
module String =
open System.Globalization
let rev s =
seq {
let rator = StringInfo.GetTextElementEnumerator(s)
while rator.MoveNext() do
yield rator.GetTextElement()
}
|> Array.ofSeq
|> Array.rev
|> String.concat ""
String.rev "\uD800\uDC00\u0061\u0300\u00C6"
Nie mogę uwierzyć, że nikt tutaj nie zapewnia ogólnego rozwiązania tego problemu!
Generic reverse with O(n) runtime.
Następnie wystarczy użyć:
let rec revAcc xs acc =
match xs with
| [] -> acc
| h::t -> revAcc t (h::acc)
let rev xs =
match xs with
| [] -> xs
| [_] -> xs
| h1::h2::t -> revAcc t [h2;h1]
let newValues =
values
|> Seq.toList
|> rev
|> List.toSeq
newValues
To właśnie F # jest o!
Czy jakieś rozwiązanie pod adresem http://cs.hubfs.net/forums/thread/4324.aspx przemawia do Ciebie? – Gabe