iso tank

丸付き数字から丸囲み数字(丸数字)に変換

洋々亭にて、様々なVBAコードが公開されている。

この中に含まれている、丸付き数字(「○2」など)を丸囲み数字(「②」など)に変換する関数を、⓪~㊿まで対応できるように改造した。

ちなみになんでこんな関数が必要かというと、一部の法令(児童福祉法など)は、項番号の代わりに丸数字を使用していることがあるが、丸数字は機種依存文字(環境依存文字)なので正しく表示されないコンピュータが存在する恐れがあり、e-Govでは「○2」としている。これを本来意図するところの丸数字に置き換えるため。

現在ではほとんどの機種(環境)で丸数字が表示できるようになっており、これを一括変換したいという要望があったので、洋々亭のコードを拝借し、かつ範囲を広げた。

'---------------------------------------------------------------------------------------------------
' 丸付き数字置換関数
' ◆機能の説明
'   ・「○3」形式の表記を「③」に変換する
'   ・一部文字列はUnicode文字に変換する
'   ・一定数を超える数字の場合は丸囲み数字が使用できないため、角かっこで囲む(○51→[51])
'   ※項番号を上記の方法で表記している法令があるための対処(児童福祉法等)。
' オリジナル:洋々亭 2006
'---------------------------------------------------------------------------------------------------
Private Function ToCircledNum(ByRef srcStr As String) As String
    
    Dim srcLen As Long      '引数の文字列長(○を含む)
    Dim tmpAsc As String    '引数から○を除去し半角英数に変換した文字列
    Dim chrNum As Long      '変換後の数字
    Dim i As Long           'イテレータ
    
    'まず全角数字を半角数字に置き換え、次に半角数字を丸数字に置き換える。
    Let srcLen = Len(srcStr)
    If srcLen < 2 Then
        Exit Function
    End If
    Let tmpAsc = String$(srcLen - 1, vbNullChar)
    For i = 2 To srcLen
        Mid(tmpAsc, i - 1, 1) = Mid$("0123456789", CLng(InStr("0123456789", Mid$(srcStr, i, 1))), 1)
    Next i
    Let chrNum = CLng(tmpAsc)
    Select Case chrNum
    Case Is < 0                                 '0未満は非対応([]で括る)
        Let ToCircledNum = "[" & tmpAsc & "]"
    Case 0                                      '丸0  = U+24EA( 9450) chrNum=0のためそのまま
        Let ToCircledNum = ChrW(9450)
    Case Is < 21                                '1~20は普通に丸囲み数字が使用できる
        Let ToCircledNum = Mid$("①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳", chrNum, 1)
    Case Is < 36                                '丸21 = U+3251(12881) 21から始まるため 12881 - 21 = 12860
        Let ToCircledNum = ChrW(12860 + chrNum)
    Case Is < 51                                '丸36 = U+32B1(12977) 36から始まるため 12977 - 36 = 12941
        Let ToCircledNum = ChrW(12941 + chrNum)
    Case Else                                   '51以上も非対応([]で括る)
        Let ToCircledNum = "[" & tmpAsc & "]"
    End Select
End Function

「引数には丸記号+全角英数字の組み合わせの文字列しか来ない」という前提で作ってる関数なので、呼び出し前に正規表現やLikeマッチングでのふるい落としが必要。簡単な判定は19行目でやってるけど(引数が1文字以下なら何も返さず終了)。

やってることは洋々亭のmarumoji関数と一緒。まず全角英数字を半角英数字に置き換え(22~25行目)、整数型に変換(26行目)。あとはSelect Case文で0未満と50超は対応する丸数字がないため[]で括る、⓪と㉑~㊿はChrW関数で文字コードから文字を取得。①~⑳は文字を並べてMid関数で対応する数字の位置に該当する文字を取得。

①~⑳もChr関数を使えば取得できるが、文字を並べてMid関数で取得した方がちょっとだけ早い。

Public Sub SpeedTest()
    Dim testNums(2000000) As Long
    Dim dummyStr As String
    Dim procTime As Single
    Dim i As Long
    For i = LBound(testNums) To UBound(testNums)
        Let testNums(i) = RandomLong(1, 20)
    Next i
    Let procTime = Timer
    For i = LBound(testNums) To UBound(testNums)
        Let dummyStr = Mid$("①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳", testNums(i), 1)
    Next i
    Debug.Print "Test1: " & Timer - procTime        'Test1: 0.140625
    Let procTime = Timer
    For i = LBound(testNums) To UBound(testNums)
        Let dummyStr = Chr(34623 + testNums(i))
    Next i
    Debug.Print "Test2: " & Timer - procTime        'Test2: 0.171875
End Sub

Private Function RandomLong(ByRef minVal As Long, ByRef maxVal As Long, Optional ByRef doRandomize As Boolean = True) As Long
    If doRandomize Then
        Randomize
    End If
    Let RandomLong = Int(Rnd * (maxVal - minVal + 1)) + minVal
End Function

⓪~㊿を全部並べてMid関数で取得した方が分かりやすいしできるならそうしたいんだけど、VBAは内部的にUnicode文字を使用しているものの、VBEはUnicode文字をサポートしていない

ChrW関数などを使用してUnicode文字をサポートしている出力先(例えばWord文書など)に出力すること自体は可能なので、こういった書き方になった。もっとスマートな方法はないだろうか。

highlight.jsを導入してみる

別になんてこたないんだけどね。

highlightjs.orgというサイトで公開されているライブラリを使用すれば、ブログ記事とかにプログラムのコードを貼り付けると自動的にシンタックスハイライトをしてくれるらしい。

blosxomのプラグインとしてコードをシンタックスハイライトする奴を作ろうかなー、とか思ってたけど既にあったわ。という話。

というわけで、ちょっと実験。

Option Explicit

'---------------------------------------------------------------------------------------------------
' クイックソート関数(SourceArray   <配列>)
'   説明: 渡された配列の要素をクイックソート(非安定ソート)で昇順に並び替える。
'---------------------------------------------------------------------------------------------------
Public Function QuickSort(ByVal SourceArray As Variant) As Variant
    
    Dim valLEnd As Variant      '左端の値
    Dim valREnd As Variant      '右端の値
    Dim valMed As Variant       '中央値
    Dim valSwap As Variant      'バッファ(値交換用)
    Dim posLEnd As Long         '左端の位置
    Dim posREnd As Long         '右端の位置
    Dim posLEnds() As Long      '左端の位置を記憶するスタック(配列)
    Dim posREnds() As Long      '右端の位置を記憶するスタック(配列)
    Dim topStack As Long        'スタック(配列)の最大添字 ※スタック確保用
    Dim bottomStack As Long     'スタック(配列)の最小添字 ※Option Base 1 への対策用(念の為)
    Dim ptrStack As Long        'スタック(配列)のポインタ
    Dim ptrLtoR As Long         '左端から右に向かって探索するポインタ(Lポインタと呼称)
    Dim ptrRtoL As Long         '右端から左に向かって探索するポインタ(Rポインタと呼称)
    
    '初期処理
    If Not IsArray(SourceArray) Then
        Exit Function
    End If
    Let posLEnd = LBound(SourceArray)   '引数の左端の位置
    Let posREnd = UBound(SourceArray)   '引数の右端の位置
    Let bottomStack = LBound(Array())   'スタックの最小添字
    Let topStack = Int(Log(posREnd - posLEnd + 1) / Log(2)) + bottomStack  'スタックサイズはLog2(n)個で足りる
    ReDim posLEnds(topStack)            'スタック確保
    ReDim posREnds(topStack)
    Let ptrStack = bottomStack          '全体範囲をスタックにPUSH
    Let posLEnds(ptrStack) = posLEnd
    Let posREnds(ptrStack) = posREnd
    
    'メイン処理
    Do Until ptrStack < bottomStack     'すべてのスタックが処理されるまで繰り返す
        
        '探索範囲POP・値取得
        Let posLEnd = posLEnds(ptrStack)    'スタックから両端の位置を取り出す
        Let posREnd = posREnds(ptrStack)
        Let ptrLtoR = posLEnd               '両端の位置をLポインタ・Rポインタにそれぞれセット
        Let ptrRtoL = posREnd
        Let valLEnd = SourceArray(ptrLtoR)  '両端の値と中間位置の値を取得
        Let valREnd = SourceArray(ptrRtoL)
        Let valMed = SourceArray(ptrLtoR + Int((ptrRtoL - ptrLtoR) / 2))
        
        '中央値導出(左端の値・右端の値・中間位置の値、の3値から中央値を求める)
        If valLEnd > valMed Then
            If valREnd > valLEnd Then
                Let valMed = valLEnd
            ElseIf valREnd > valMed Then
                Let valMed = valREnd
            End If
        ElseIf valLEnd > valREnd Then
            Let valMed = valLEnd
        ElseIf valMed > valREnd Then
            Let valMed = valREnd
        End If
        
        '探索・値交換
        Do Until ptrLtoR > ptrRtoL          'LポインタとRポインタの位置が逆転するまで繰り返す
            Do Until SourceArray(ptrLtoR) >= valMed
                Let ptrLtoR = ptrLtoR + 1   '左から右へ、中央値以上の値の位置を探索する
            Loop
            Do Until SourceArray(ptrRtoL) <= valMed
                Let ptrRtoL = ptrRtoL - 1   '右から左へ、中央値以下の値の位置を探索する
            Loop
            If ptrLtoR > ptrRtoL Then
                Exit Do                     'LポインタとRポインタの位置が逆転したら探索・値交換を終了する
            End If
            If ptrLtoR < ptrRtoL Then       'Lポインタ<Rポインタ であれば、左の値と右の値を交換する
                Let valSwap = SourceArray(ptrLtoR)  '(左の値は中央値以上、右の値は中央値以下になっている)
                Let SourceArray(ptrLtoR) = SourceArray(ptrRtoL)
                Let SourceArray(ptrRtoL) = valSwap
            End If
            Let ptrLtoR = ptrLtoR + 1       'Lポインタ≦Rポインタ であれば、範囲を1つずつ狭める
            Let ptrRtoL = ptrRtoL - 1
        Loop
        
        '探索範囲の分割・交換(左範囲[左端~交差したRポインタ] 右範囲[交差したLポインタ~右端])
        If ptrRtoL - posLEnd < posREnd - ptrLtoR Then   '※ポインタの間に要素がある場合、その要素の位置は確定
            Let ptrLtoR = ptrLtoR Xor posLEnd   '左範囲と右範囲で、範囲の広い方を先にスタックするように調整
            Let posLEnd = ptrLtoR Xor posLEnd   '何もなければ左範囲→右範囲の順でスタックするようになっている
            Let ptrLtoR = ptrLtoR Xor posLEnd   '右範囲の方が左範囲より広ければ、左右の端の位置とポインタを交換
            Let ptrRtoL = ptrRtoL Xor posREnd   '左端←→Lポインタ、Rポインタ←→右端
            Let posREnd = ptrRtoL Xor posREnd   'スタックのサイズをLog2(n)以下に抑えられるようになる
            Let ptrRtoL = ptrRtoL Xor posREnd
        End If
        
        '探索範囲(広い方)PUSH
        If posLEnd < ptrRtoL Then               '範囲内の要素が2以上あればスタックにPUSHする
            Let posLEnds(ptrStack) = posLEnd    '(1つしかない場合はその要素の位置が確定したためPUSHしない)
            Let posREnds(ptrStack) = ptrRtoL    '現在処理中のスタックは上書きされる(実質的にPOPと同じ動作)
            Let ptrStack = ptrStack + 1         'スタックポインタに1加算=次の空きスタックへ移動
        End If
        
        '探索範囲(狭い方)PUSH
        If ptrLtoR < posREnd Then               '上記と同じ処理
            Let posLEnds(ptrStack) = ptrLtoR
            Let posREnds(ptrStack) = posREnd
            Let ptrStack = ptrStack + 1
        End If
        
        '最上位スタックへポインタ移動
        Let ptrStack = ptrStack - 1
    Loop
    Let QuickSort = SourceArray
End Function

コードは前に何かで使ったクイックソート。さて、どうかな?

雑踏の中の声

久しぶりに書いてみる。興味深いニュースがあったので。

騒々しい場所で会話が聞き取れないのは「隠れた難聴」であるとする研究結果が発表される - GIGAZINE

ADHDとアスペルガー症候群(と、副次的な鬱)を患っているけど、まさしくこの例に当てはまる。本当に声が聞き取れない。隣の人と話すにも、相当耳を近づけないと聞き取れないこともある。

車やバスに乗っている時もそうで、前や後ろの座席に座っている人の会話はほぼ聞き取れない。

担当医によると、音声を選択的に聴取する機能(カクテルパーティ効果)が働いていない、とのこと。

聴力検査では特に異常はない。音楽も普通に聴ける。オーケストラでもバンドでも。ただし、自分が「普通に聴けていると思っている」だけで、「聞こえ方」が他の人と違うのかどうかはわからない(クオリア? なのか?)。

オーケストラの音の中から「クラリネットの音」とか「ヴァイオリンの音」だけを聴き取ることも、普通にできている・・・と思っている。や、さすがに「あそこに座っている人の」とかいうのは無理。たぶん。

恐らく人間の「話し言葉」と楽器の奏でる「音」とでは、音そのものの複雑さ(周波数?)や、その音の組み合わせ(言葉やメロディ)が持つ意味の複雑さなどでは、人間の話し言葉の方が段違いに複雑だからなのだろうと思う。

まして複数人がそれぞれに会話している状況で、その中から特定の会話だけを抜き出せって? できるわけねえだろ! って思ってました。

でもよく考えてみたら、周りの人は普通にしてるんだよね。・・・うん、正直に言うね。お前ら全員、聖徳太子の生まれ変わりかなにかなの? って思いましたよ。これに気付いた時。心の底から。本当ですよ? 掛け値なしに。それぐらいあり得ないと思ったんです。

でもよく考えてみたら、聖徳太子は「一度に10人の会話を聞き取ることができる」のであって、「10人の会話からある2人の間の会話だけを抜き取って聞き取る」わけではないんですよねコレ。・・・ちなみに皆さん、実は10人の会話を把握できるんだけど面倒だから2人の会話だけ聴き取っていることにしているわけではないですよね・・・?

で、

これによりかなりコミュニケーションの色々な部分に弊害が出ているわけです。

周りが何を話しているか解らない → とにかくなにかコミュニケーションを取りたい → ぶっ込んでみる → 違う、そうじゃない

コレに気付いた時、あれ・・・? これ、俺、周りにすげえ迷惑かけてたんじゃね・・・?ってなりまして、ちょっと色々アレになって、うん、まぁ・・・

・・・うん、今なおまだアレはアレしてますが・・・そもそも気付けたっていうだけで、何も治っていないのですが・・・

・・・身についてしまった癖ってなかなか直らないんです・・・ましてや発達障害もあって「正しい形」が解らなくて尚更。

本題。

俺の場合、特に飲み会のような場所だと誰の何の話を聞いたらいいのかわからない状態になってしまうのと、そもそも飲み会以外の場合でも、集中して話を聞こう聞こうとすればするほど余計聞こえなくなる気がします。

担当医曰く、その話を全部聞こうとするんじゃなくて、要点だけでも聞き取れていれば、そこから話の全体像が掴めるようになってくる、もの、らしい・・・(健常者であれば)

ただし、発達障害もこの症状もまだ研究が進んでいない分野なので、上の2chのスレッドの>>1-2にあるように「音声の解像度」が著しく低いために音声の区別ができなくなっているのか、もっと違うところの「音声を選択する機能」に問題があるのか、またその治療法についても、ほとんど解明されていない。

解っているのは、発達障害者でこの症状がある人にはほぼ対処法がなく、トレーニングや気合いでどうこうできるものではない、ということ。

せめて一般論の対処法として、

  1. 静かな場所に身を置いたり、休憩時に頭の中をリセットする(何も考えないようにする)などして、神経の疲れを取る。
  2. 規則正しい生活リズムを維持し、しっかり睡眠・休息を取る。休日でも何もしない時間を作り、頭の中をリセットする。

これぐらいしか、できることが、ない。

トレーニング法がないわけでもないとのことで、聞いてみた。が、無理は絶対にするな、やってみてハッキリ効果があるとかトレーニングを持続できる(=辛くない)のであれば、という条件付きで、と念を押された

――健常な人でも1から10まで全部聞こえているとは限らない。先にも書いたけど無理に全部聞こうとしないで、聞こえるところだけ拾って残りは補完/予測している。それと同じことをすればいい。

無理に全体を聞き取ろうとしないで、聞こえる部分だけ聴き取る。聞こえなかった部分は補完/予測する。解らなかったり間違ってたりしたらちゃんと謝って(重要)、聞き返す。これを繰り返すことで、もしかしたら脳が学習して少しでも聞き取れるようになるかもしれない、らしい。

そういや、健常な人は会話をしながら相手の「話し方のクセ」などを自然に学習して、同じ相手であれば自然と補完/予測の精度が上がるらしいです。

担当医に「一番重要」と言われたのは「悩みすぎない」こと。

ゴチャゴチャした中であれこれ話しかけられたら、どんな人にだって許容量があるし、人によって差があるのも当然のこと。

そんな程度のことで怒ったり、もし聞き取れない性質であることを素直に打ち明けてもそれを疑うような人であれば、そんな人とは関わらない方が良い。

もしその相手が上司なら、部下のそういった性質をきちんと把握する義務がある立場の人間であって、それを疎かにするようであればその程度の上司。

その人の理解を得ないと今後の人生に差し障りがあるほどに大切な人であれば、その人に連れ添ってもらってしかるべき医療機関にかかることを勧めたい。

雑踏の中で話し声が聞こえないだけで死ぬわけじゃない。これまでも生きてきた。これからも生きていける。

できないものはできないとして、周囲となんとか折り合いをつけて生きていくしかない。

それしかないのだと思う。

が。

嫌われるのがものすごく辛い。

万人に好かれる人間になれないのは当たり前。でも嫌われているのが解るのは死にたくなるほど辛い。そして嫌われている原因なんて解らないことがほとんどだし、解ったとしても解決できることの方が少ない。

色んな事を考える度に、何度でも何度でも改めて痛感させられるし、その度に本当に辛くなる。自分は生きることに向いていない。

曰く、「余計な事を考えすぎている」らしい。『杞國 有人憂天地崩墜 身亡所寄 廢寢食者』ってそういや中学だかで習ったっけな・・・

カテゴリ名変更&新しい楽器がふえたよ

この度ティンホイッスル以外の新しい楽器を購入し、こちらについてもティンホイッスルと同等に力を入れていくこととした。

このため、カテゴリ名を「ティンホイッスル」から「Feadóg」に変更した。

なぜこの名前なのかというと、「Feadóg」というティンホイッスルのメーカーがあるのだが、これは"feadóg"という言葉がアイルランドで「笛」を指すからだ。

まぁつまり、ちょっと前に「アイリッシュ・フルート」という新しい楽器を買ったのでカテゴリ名を修正しようと思ったのだが、カテゴリ名が「Tin Whistle & Irish Flute」では長すぎるし、かといって「Whistle & Flute」ではなんだかぴんとこないかもしれない、と考え、「笛」を意味するアイルランド語でいいやという流れになったのである。

ちなみに実はこの他にも篠笛を3本ほど持っていたりするのだが、これもアイルランド語では「Feadóg」である(ハズ)。

ちなみにこれがアイリッシュフルート。
hammy_hamilton_practice_flute_s.jpg

なんかアルミパイプとプラスチックに穴をあけただけに見えるが、こんなんでも音はかなりしっかり出る。

めっちゃ楽しい。めっちゃ家族に聞かれてて恥ずかしMAXだけど。

blosxomとさくらのレンタルサーバのApacheにまつわる問題点

書いておかないと後で''ぜってー忘れる''ので、忘れないうちにメモしておく。

というか過去の記事を漁るともしかすると過去に同じ問題に遭遇していたかもしれないけど探すの面倒なのでやめた。

1.環境変数を追加できない
Apacheには環境変数モジュール(mod_env)というものがあり、これで環境変数を制御しているが、さくらのレンタルサーバではCGIを実行する際の環境変数は制御できない。
ホストにログインした際の環境やCRONを実行する際の環境は制御できる。
blosxomでユーザー環境変数を使いたかったのだが諦めた。さくらのレンタルサーバはsuExec環境なので当然と言えば当然であるが、すっかりド忘れしていたのでアホだ。
2.MultiViewsオプションを無効化できない
MultiViewsとはApacheの「コンテントネゴシエーション」モジュールで提供されている機能の一つ(多分)で、リクエストURIにファイル名のみ(拡張子を除いて)しかなく、かつ同名のディレクトリが存在しなかった場合、該当ディレクトリ内の同名の拡張子付きファイルを勝手に探し出してページに表示してしまう機能である。
例を挙げると、"http://hogefuga.jp/test/unko"をリクエストしたが"unko"ディレクトリが存在せず、"test"ディレクトリ内に"unko.conf"ファイルが存在した場合、「あ、これきっと"unko.conf"ファイルのことなんだね」とか勝手に解釈してファイルの中身を表示したりする。
"unko.conf"、"unko.cgi"、"unko.log"等と複数のファイルがあった場合、「たぶんこのユーザーは"unko.cgi"がほしいんじゃないかな」とかその都度選別してファイルの中身を表示したりCGIを実行したりする。
なお、末尾にスラッシュついてようがついていまいがお構いなしに勝手にファイルとして解釈する。
さくらのレンタルサーバの場合、Optionsがユーザー側で設定できないため、無効化することすらできない。
なおMultiViewsは基本的にMIMEが認識している拡張子を探し出す、という動作をしていた。なのでそこんとこをなんとか無効化できないかとあれこれ試してみたがどれもうまくいかなかった。
MultiviewsMatchというOptionは操作できるが、これは要はマッチタイプを追加する機能であって、減らすことはできない。
リライトモジュール(mod_rewrite)で上書きできないかと試してみたが、RewriteよりMultiViewsが優先されており、先にファイルにアクセスされてしまった。どうしようもない。
これはもう単純に、''ディレクトリ内に同名のファイルとディレクトリは作らない''ことで回避するしかない。
なおblosxomの場合、「CGIを設置しているディレクトリ内のファイル名」と、「エントリを保存しているディレクトリ内のサブディレクトリ名」が重複しないようにしなければならないので、注意すること。めどい。
なるべくblosxomの設定ファイル名を変えずに使いたかったのだが諦めた。早々に諦めればよかったのにあれこれと抗った挙げ句に結局できなかったのでアホだ。

ま、何が言いたいのかと言うと blosxom 2.1.2 を導入しようとしてなるべくblosxom.cgiを改変することなく使いたかったのだができなかったのでアホだ。

2019/08/29 追記

さくらのレンタルサーバの仕様変更によりMultiViewsは使用可能になった