2015-07-19 10 views
5

Mentre posso parole con successo sorta spagnoli con le vocali accentate specificando una localizzazione UTF-8 all'interno di std :: sort,Sort Mappa (spagnolo) accentuato parole Rcpp

// [[Rcpp::export]] 
std::vector<std::string> sort_words(std::vector<std::string> x) { 
    std::sort(x.begin(), x.end(), std::locale("en_US.UTF-8")); 
    return x; 
} 

/*** R 
words <- c("casa", "árbol", "zona", "árbol", "casa", "libro") 
sort_words(words) 
*/ 

returns (as expected): 
[1] "árbol" "árbol" "casa" "casa" "libro" "zona" 

non riesco a capire come di fare lo stesso con una mappa:

// slightly modified version of tableC on http://adv-r.had.co.nz/Rcpp.html 
// [[Rcpp::export]] 
std::map<String, int> table_words(CharacterVector x) { 
    std::setlocale(LC_ALL, "en_US.UTF-8"); 
    // std::setlocale(LC_COLLATE, "en_US.UTF-8"); // also tried this instead of previous line 
    std::map<String, int> counts; 
    int n = x.size(); 
    for (int i = 0; i < n; i++) { 
    counts[x[i]]++; 
    } 
    return counts; 
} 

/*** R 
words <- c("casa", "árbol", "zona", "árbol", "casa", "libro") 
table_words(words) 
*/ 

returns: 
casa libro zona árbol 
    2  1  1  2 

but I want: 
árbol casa libro zona  
    2  2  1  1 

Tutte le idee su come avere table_words mettere l'accento "árbol" prima "casa", con Rcpp o addirittura di nuovo fuori in R, con base::sort?

Inoltre, std::sort(..., std::locale("en_US.UTF-8")) solo parole sulla mia macchina Linux con: gcc versione 4.8.2 (Ubuntu 4.8.2-19ubuntu1). Non funziona su Mac 10.10.3 con: Apple LLVM versione 6.1.0 (clang-602.0.53) (basato su LLVM 3.6.0svn). Qualche indizio su cosa manca al mio compilatore Mac che il mio compilatore Linux ha?

Ecco il mio script e la mia sessionInfo, per entrambe le macchine:

// [[Rcpp::plugins(cpp11)]] 
#include <locale> 
#include <clocale> 
#include <Rcpp.h> 
using namespace Rcpp; 

// [[Rcpp::export]] 
std::vector<std::string> sort_words(std::vector<std::string> x) { 
    std::sort(x.begin(), x.end(), std::locale("en_US.UTF-8")); 
    return x; 
} 

// [[Rcpp::export]] 
std::map<String, int> table_words(CharacterVector x) { 
    // std::setlocale(LC_ALL, "en_US.UTF-8"); // tried this instead of next line 
    std::setlocale(LC_COLLATE, "en_US.UTF-8"); 
    std::map<String, int> counts; 
    int n = x.size(); 
    for (int i = 0; i < n; i++) { 
    counts[x[i]]++; 
    } 
    return counts; 
} 

/*** R 
words <- c("casa", "árbol", "zona", "árbol", "casa", "libro") 
sort_words(words) 
table_words(words) 
sort(table_words(words), decreasing = T) 
output_from_Rcpp <- table_words(words) 
sort(names(output_from_Rcpp)) 
*/ 

> words <- c("casa", "árbol", "zona", "árbol", "casa", "libro") 

> sort_words(words) 
[1] "árbol" "árbol" "casa" "casa" "libro" "zona" 

> table_words(words) 
casa libro zona árbol 
    2  1  1  2 

> sort(table_words(words), decreasing = T) 
casa árbol libro zona 
    2  2  1  1 

> output_from_Rcpp <- table_words(words) 

> sort(names(output_from_Rcpp)) 
[1] "árbol" "casa" "libro" "zona" 

sessionInfo on linux machine: 
R version 3.2.0 (2015-04-16) 
Platform: x86_64-pc-linux-gnu (64-bit) 
Running under: Ubuntu 14.04 LTS 

locale: 
[1] en_US.UTF-8 

attached base packages: 
[1] stats  graphics grDevices utils  datasets methods base  

loaded via a namespace (and not attached): 
[1] tools_3.2.0 Rcpp_0.11.6 

sessionInfo on Mac: 
R version 3.2.1 (2015-06-18) 
Platform: x86_64-apple-darwin13.4.0 (64-bit) 
Running under: OS X 10.10.3 (Yosemite) 

locale: 
[1] en_US.UTF-8 

attached base packages: 
[1] stats  graphics grDevices utils  datasets methods base  

other attached packages: 
[1] textcat_1.0-3 readr_0.1.1 rvest_0.2.0 

loaded via a namespace (and not attached): 
[1] httr_1.0.0 selectr_0.2-3 R6_2.1.0  magrittr_1.5 tools_3.2.1 curl_0.9.1 Rcpp_0.11.6 slam_0.1-32 stringi_0.5-5 
[10] tau_0.0-18 stringr_1.0.0 XML_3.98-1.3 
+1

Perdono la mia ignoranza, ma quando ha fatto 'std :: sort' prendere un terzo parametro che era un locale? Il terzo parametro di 'std :: sort' dovrebbe essere una funzione o un functor che confronta due elementi, non una locale. – PaulMcKenzie

+1

@PaulMcKenzie: Una localizzazione è, tra le altre cose, un functor che confronta due elementi. http://en.cppreference.com/w/cpp/locale/locale/operator() –

+1

Non so nulla di "Rcpp", ma sei consapevole che per un 'std :: map', l'ordinamento fa parte di il tipo stesso e che hai bisogno di un comparatore personalizzato per abilitare un ordine diverso? –

risposta

1

Non ha senso applicare std::sort su un std::map, perché una mappa è sempre ordinato, per definizione. Quella definizione è parte del tipo concreto istanziato dal modello. std::map ha un terzo parametro di tipo "nascosto" per la funzione di confronto utilizzata per ordinare le chiavi, che per impostazione predefinita è std::less per il tipo di chiave. Vedi http://en.cppreference.com/w/cpp/container/map.

Nel tuo caso, puoi utilizzare std::locale come tipo di confronto e passare std::locale("en-US") (o qualsiasi altra cosa si adatti al tuo sistema) al costruttore.

Ecco un esempio. Usa C++ 11, ma puoi usare facilmente la stessa soluzione in C++ 03.

#include <map> 
#include <iostream> 
#include <string> 
#include <locale> 
#include <exception> 

using Map = std::map<std::string, int, std::locale>; 

int main() 
{ 
    try 
    { 
     Map map(std::locale("en-US")); 
     map["casa"] = 1; 
     map["árbol"] = 2; 
     map["zona"] = 3; 
     map["árbol"] = 4; 
     map["casa"] = 5; 
     map["libro"] = 6; 

     for (auto const& map_entry : map) 
     { 
      std::cout << map_entry.first << " -> " << map_entry.second << "\n"; 
     } 
    } 
    catch (std::exception const& exc) 
    { 
     std::cerr << exc.what() << "\n"; 
    } 
} 

uscita:

árbol -> 4 
casa -> 5 
libro -> 6 
zona -> 3 

Naturalmente, è necessario essere consapevoli del fatto che std::locale è fortemente dipendente dall'implementazione. Forse starai meglio con Boost.Locale.

Un altro problema è che questa soluzione può sembrare confusa, perché un std::locale non è esattamente qualcosa che molti programmatori associano a una funzione di confronto. È quasi un po 'troppo intelligente.

Quindi un forse più leggibile alternativa:

#include <map> 
#include <iostream> 
#include <string> 
#include <locale> 
#include <exception> 

struct ComparisonUsingLocale 
{ 
    std::locale locale{ "en-US" }; 

    bool operator()(std::string const& lhs, std::string const& rhs) const 
    { 
     return locale(lhs, rhs); 
    } 
}; 

using Map = std::map<std::string, int, ComparisonUsingLocale>; 

int main() 
{ 
    try 
    { 
     Map map; 
     map["casa"] = 1; 
     map["árbol"] = 2; 
     map["zona"] = 3; 
     map["árbol"] = 4; 
     map["casa"] = 5; 
     map["libro"] = 6; 

     for (auto const& map_entry : map) 
     { 
      std::cout << map_entry.first << " -> " << map_entry.second << "\n"; 
     } 
    } 
    catch (std::exception const& exc) 
    { 
     std::cerr << exc.what() << "\n"; 
    } 
} 
+0

Grazie per l'aiuto esteso, ma ancora senza fortuna: Earls-MBP: C++ earlbrown $ 'g ++ -std = C++ 11 order_with_accents.cpp -o go' Earls-MBP: C++ earlbrown $'./Go' 'collate_byname :: collate_byname non è riuscito a costruire per en-US' Earls-MBP: C++ earlbrown $' g ++ -v' 'Configurato con: --prefix =/Applications/Xcode.app/Contents/Developer/usr - with-gxx-include-dir =/usr/include/C++/4.2.1 Apple LLVM versione 6.1.0 (clang-602.0.53) (basato su LLVM 3.6.0svn) Destinazione: x86_64-apple-darwin14.4.0 Filetto: posix' –