配列のshuffle
以前、shuffleをPowerShellで実装したが冗長だっため、再び調べてみたら、すごく簡単な方法が見つかった。
下記のページに記載されている。
Get-Random
どうも -Count パラメータを指定すると、入力をshuffleするようだ。
PS > 1..10 | Get-Random -Count ([int]::MaxValue) 6 7 5 2 4 1 8 10 3 9
ちなみに、shuffleはリストをランダムでいくつか取得した際に使うため、たまに使いたくなる。
Outlook の Web APIを呼び出す例
Outlook の Web APIを呼び出すサンプルコードを書いた。
起動して http://localhost:8080/ にアクセスすると、
OAuth2のためにOutlookに転送され、許可すると、
http://localhost:8080に戻ってきて、
メッセージのタイトル一覧を表示する。
Outlook の Web APIを呼び出す方法は下記に記載あり。OAuth2でアクセストークンを取得し、APIを呼び出す。
Get Started with the Outlook REST APIs - Outlook Dev Center
使用したパッケージ
- base >= 4.7 && < 5
- Spock >= 0.11
- mtl
- text
- hoauth2
- http-types
- http-client
- bytestring
- http-client-tls
- aeson
{-# LANGUAGE OverloadedStrings #-} module Main where import Web.Spock import Web.Spock.Config import Network.OAuth.OAuth2 import Control.Monad import Control.Monad.Trans import Data.IORef import qualified Data.Text as T import qualified Data.Text.Encoding as TE import qualified Data.ByteString.Lazy as BL import qualified Data.ByteString as B import qualified Network.HTTP.Types.URI as URI import qualified Network.HTTP.Types.Header as Header import qualified Network.HTTP.Client as HTTPClient import qualified Network.HTTP.Client.TLS as TLS import qualified Data.Aeson as Aeson data MySession = EmptySession data MyAppState = DummyAppState (IORef Int) main :: IO () main = do ref <- newIORef 0 spockCfg <- defaultSpockCfg EmptySession PCNoDatabase (DummyAppState ref) runSpock 8080 (spock spockCfg app) oauth2Param :: OAuth2 oauth2Param = OAuth2 { oauthClientId = "<client_id>", oauthClientSecret = "<client_secret>", oauthOAuthorizeEndpoint = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize", oauthAccessTokenEndpoint = "https://login.microsoftonline.com/common/oauth2/v2.0/token", oauthCallback = Just "http://localhost:8080/" } scope = "https://outlook.office.com/mail.read" redirectHotmail = redirect $ TE.decodeUtf8 $ appendQueryParam (authorizationUrl oauth2Param) [("scope", scope)] getAccessToken code = do man <- HTTPClient.newManager TLS.tlsManagerSettings at <- fetchAccessToken man oauth2Param $ TE.encodeUtf8 code return $ TE.decodeUtf8 $ either BL.toStrict accessToken at mail = "https://outlook.office.com/api/v2.0/me/messages" mailparam = [ ("$select", Just "Subject,From,ReceivedDateTime"), ("$top", Just "25"), ("$orderby", Just "ReceivedDateTime DESC")] getMailBox :: T.Text -> IO BL.ByteString getMailBox accessToken = do man <- HTTPClient.newManager TLS.tlsManagerSettings requestUrl <- HTTPClient.setQueryString mailparam <$> HTTPClient.parseRequest mail let request = requestUrl { HTTPClient.method = "GET", HTTPClient.secure = True, HTTPClient.requestHeaders = [(Header.hAuthorization, B.append "Bearer " $ TE.encodeUtf8 accessToken)]} res <- HTTPClient.httpLbs request man return $ HTTPClient.responseBody res data MailMessage = MailMessage { subject :: T.Text } deriving Show instance Aeson.FromJSON MailMessage where parseJSON (Aeson.Object v) = MailMessage <$> (v Aeson..: "Subject") parseJSON _ = mzero data MailBox = MailBox { value :: [MailMessage] } deriving Show instance Aeson.FromJSON MailBox where parseJSON (Aeson.Object v) = MailBox <$> (v Aeson..: "value") parseJSON _ = mzero getMailMessages code = liftIO (getAccessToken code >>= getMailBox) >>= toHtml where toHtml body = html (maybe "" (T.unwords . map (T.append "<BR>" . subject) . value) (Aeson.decode body)) app :: SpockM () MySession MyAppState () app = get root $ param "code" >>= maybe redirectHotmail getMailMessages
24
24. ファイル参照の抽出
記事から参照されているメディアファイルをすべて抜き出せ.
メディアファイルというのが何を指しているのかわからなかったが、
ファイル参照の抽出というタイトルから、File:を抜き出すことにした。
中身を見ていると、日本語のファイルでも良さそうだったが、面倒だったためFileのみとした。
import Data.Aeson as A import Control.Applicative import System.Environment import Data.Maybe import Data.ByteString.Lazy.UTF8 as B import GHC.Generics import Text.Regex.Posix import Prelude as P import GHC.IO.Encoding data Article = Article { text :: String, title :: String } deriving (Eq, Show, Generic) instance FromJSON Article main = do setLocaleEncoding utf8 filename <- head <$> getArgs body <- fromString <$> readFile filename writeFile "24.out.txt" $ unlines $ map (P.drop 5) $ filter (\x -> P.length x /= 0 ) $ concatMap concat $ map f $ map (text . fromJust . A.decode) $ B.lines $ body where f = ((=~ ("File:[^]|]*" :: String)) :: String -> [[String]])
[^]|]と[^|]]で異なる解釈となることが勉強になった。
Windows版HaskellでUTF8のファイル読み書き
Haskell Platform 7.10.2-aをインストールした際に、日本語(utf8)の扱いがわからなくなってしまって、ずっとほっといたのだが、ふと思い出して調べてみた。
紆余曲折したが、ポイントとしては下記の2点。
- Handleにutf8を指定
ファイルの読み書きなどのデフォルトのエンコーディングがcp932になっているため、utf8のファイルを読み込む場合は、Handleのエンコーディングをutf8に変更する
- ByteStringを扱う場合は、utf8-stringモジュールを使う
ただし、Stringで読みこんだ文字列をByteStringに変換して使う。標準でインストールされるByteStringモジュールに存在するreadFileなどの関数がないため。
サンプルとして、第一引数で指定したファイルをutf8の文字コードで読み込み、内容を標準出力するプログラムを記載する。
import System.Environment import System.IO main = do filename <- head <$> getArgs withFile filename ReadMode f where f h = hSetEncoding h utf8 >> hGetContents h >>= putStrLn
また、Powershellから標準出力をrunghcに渡す場合、下記のコマンドを実行する。
$OutputEncoding = [Text.Encoding]::Default
コマンドに渡す際のエンコーディングがasciiになっているため、cp932に変更する。
毎回ハンドルのエンコーディングを変更するのは面倒だという場合は、
GHC.IO.EncodingのsetLocaleEncoding関数で、デフォルト値を変更することができる。ただし、標準出力などのエンコーディングも変更されるので、注意する。