FactorでAtCoderの問題を解く
Factorとは
2025/01/02現在、AtCoder Problems上で6人しかLanguage Ownersがいないプログラミング言語です。

AtCoder Problems
https://kenkoooo.com/atcoder/#/lang
公式サイトの説明を見てみます。(以下公式サイトより引用)
The Factor programming language is a concatenative, stack-based programming language with high-level features including dynamic types, extensible syntax, macros, and garbage collection. On a practical side, Factor has a full-featured library, supports many different platforms, and has been extensively documented.
The implementation is fully compiled for performance, while still supporting interactive development. Factor applications are portable between all common platforms. Factor can deploy stand-alone applications on all platforms. Full source code for the Factor project is available under a BSD license.
日本語訳
Factorは、連結的(concatenative)かつスタックベースのプログラミング言語であり、動的型付けやマクロ、高度に拡張可能な文法、ガベージコレクションといった高水準の機能を備えています。さらに、標準ライブラリが充実しており、多くのプラットフォームをサポートしています。コンパイラによる実行時の高速化と、対話的な開発スタイルの両立も特徴の一つです。生成したアプリケーションは主要なプラットフォームに移植が可能で、スタンドアロン形式の配布にも対応しています。 公式実装やソースコードはBSDライセンスで公開されており、豊富なドキュメントが用意されています。
スタック指向型のプログラミング言語で、1つのスタックから引数を取り結果をスタックに返すということを常に意識する必要がある言語となっています。
スタック指向プログラミング言語とは何? わかりやすく解説 Weblio辞書
Factor - Wikipedia
WikipediaによるとForth等から影響を受けているようで、Forthは1位で553 ACともっと使われています(と言っても10AC以上しているのは5人ですが……)。
Forthは型付けされておらず、ガーベジコレクションもなくFactorよりも低級な言語であるということです。 Factor/FAQ/Why?
スタックの気持ちと数値演算
よくあるプログラミング言語では5+3を計算させるときは「int num = 5 + 3;」のような二つの数字の間に演算子を置くそのままの中置記法を用いますが、スタックの場合数字を下から積み上げていってスタックの上二つで演算を行うため、後置記法(逆ポーランド記法)となります。以下はFactorで5+3を計算して出力するコードです。 (+を使用するためにmathを、数字の出力のためにprettyprintをUSINGしています。)
USING: math prettyprint ; 5 3 + .
「5 3 +」の時点でスタックには結果の8が返されます。prettyprintの「.」でスタックの中身を一つ出力すると8が標準出力に出ます。

各種演算は以下の様な結果になります。複数の数字を積み重ねた場合でも上二つに対して操作をして結果を返すことを理解していれば扱えますね。ちなみにFactorでのコメントアウトは「!」です。
USING: math prettyprint kernel ; 5 3 + . ! 8 5 3 - . ! 2 5 3 * . ! 15 5 3 / . ! 1 + 2/3 5 3 / 3 * . ! 5 5 3 /i . ! 1 5 3 /f . ! 1.666666666666667 5 3 mod . ! 2 5 3 + 2 + . ! 10 = (5 + 3) + 2 5 3 2 + + . ! 10 = 5 + (3 + 2) 5 3 2 - + . ! 6 = 5 + (3 - 2) 5 3 2 - 1 + - . ! 3 = 5 - ((3 - 2) + 1) 5 3 > . ! t 5 3 = . ! f 5 5 = . ! t 5 3 <= . ! f
「=」だけkernelで他はmathで定義されています。「/」だと有理数(分数)で計算しているので「/i」「/f」と使い分ける必要があります。「t」と「f」のboolean型で返ってきている結果は後のif文で使います。
データ型
整数や分数(有理数)、小数が登場していますが、Factorは動的型付け言語です。例えば以下の様な型があります。
| 型名 | Vocabulary | 説明 |
|---|---|---|
| boolean | kernel | tとfをとる。ブール値、真と偽 |
| float | math | 小数 |
| integer | math | 整数 |
| rational | math | 有理数 |
| str | bare | 文字列、sequenceでもある |
| sequence | sequences | 長さlengthやn番目nthが定義される並び、str,array,vector等配列と見なせるものは全てsequence |
| array | arrays | { 1 2 3 }のように表される固定長の配列、nthなどはO(1) |
| vector | vectors | V{ 1 2 3 }のように表される可変長配列 |
配列は配列一個がそのままスタックの一要素として積まれます。n番目「nth」(0-indexed)、最初の1-4要素を各種返すような「first」「second」「third」「first2」(最初の二つ)「first3」(三つ)「first4」(四つ)、最初の等しい要素のindexを返す「index」*1などが使えます。
AtCoderでFactorをしてみる
AtCoderの提出言語ではFactor (Factor 0.98)が選べるのでこちらを選択してみます。現在の最新版は0.100(2024年9月11日)であり、0.98(2023年8月24日)は二つ前のバージョンですがFactorガチ勢ではないので関係ないですね。公式にいくとバイナリパッケージがダウンロードできますが、環境構築等は面倒なので気軽に試せるのでコードテスト上で実行します。
Hello, world!する
A - 1.00.はじめに
FactorでのHello, worldは以下です。
USING: io ; "Hello, world!" print
文字列をスタックに積んでそれを「print」で出力しているだけです。実はFactorは終了時にスタックが空である必要があるという条件がありますが「print」や「.」はスタックに結果を返さず、出力した中身はスタックから消えるので空になっています。

Submission #53701275 - C++入門 AtCoder Programming Guide for beginners (APG4b)
コードテストで実行していると[実行時間:メモリ]が[750 ms : 240 MB]使われる場合と上記のように[118 ms : 130 MB]の場合があって早くもよく分からないですね。(どちらにせよメモリ使用量が結構多いですが、Factor/FAQ/Why?のページをよく見ると「最低でも約 128 MB 」RAMを使用するとあります。メモリ制限の厳しい昔の問題はMLEします。)
ioの「print」は文字列(str)を出力し改行します。prettyprintの「.」でスタックの中身を出そうとすると文字列の場合「"」が前後につくのでioの「print」を使用します。
USING: prettyprint ; "Hello, world!" . ! "Hello, world!"が出力される
vocabularyとword
「USING: 」でライブラリ?をimport?して関数や機能を使えるようにしていますね。
Factorではメソッド・関数は「word」であり、全ての「word」は「IN: 」で定義されたモジュール「vocabulary」に属するように宣言する必要があります。
Factor/FAQ/Vocabulary
全てのwordは一つのvocabularyに含まれ、使用する前には明示的に検索パスにvolabularyに追加する必要があります。こちらが「USING: 」の正体です。volabulary一つだけなら「USE: 」(;はなし)でも良いですが常にUSINGで問題ないと思います。
Vocabularies cookbook - Factor Documentation
細かい中身などは公式リファレンスに全て書いてあるので分からなくなったら見ます。
「print」や標準入力から文字列を読み取る「readln」もioのwordの中に書いてあります。
io vocabulary - Factor Documentation
スタックの順番の操作
計算前の値・一度使ってしまったデータがスタックから消えてしまうと困るときや順番を変えたいとき、スタックの中身を捨てたいときなどスタックに積まれたデータを操作するメソッドがkernelに含まれています。良く使いそうなのは以下です。
| Word | 説明 |
|---|---|
| dup | スタックの上から1番目を複製 |
| 2dup | スタックの上から1番目と2番目を複製 |
| dupd | スタックの上から2番目を複製し2番目に入れる |
| over | スタックの上から2番目を複製し1番上に入れる |
| pick | スタックの上から3番目を複製し1番上に入れる |
| swap | スタックの上から1番目と2番目を交換 |
| swapd | スタックの上から2番目と3番目を交換 |
| drop | スタックの上から1番目を捨てる |
| 2drop | スタックの上から1番目と2番目を捨てる |
| nip | スタックの上から2番目を捨てる |
kernel vocabulary - Factor Documentation
入力を受け取って操作する
一行受け取る
「readln」は文字列として入力を一行受け取ってスタックに積みます。 数字として解釈したい場合はmath.parserをUSINGして「string>number」を適用します。 逆に数字の出力は「number>string」を適用すればprettyprintの「.」でなくても「print」出来ます。
USING: math math.parser io ; readln ! "42" を標準入力から受け取る string>number ! 42 2 * ! 84 number>string print ! "84"
単語間の区切りは半角スペース・改行どちらでも良いです。USINGの最後だけ「;」が必要ですが、これも前後に半角スペース・改行のどちらかが必要です。
ABC068 A問題
A - ABCxxx
受け取った数字をABCの後にくっつけて出力するだけです。
ABCの後に改行されると困るので"ABC"にはioの「write」を使います。
USING: io ; readln "ABC" write print
Submission #53704112 - AtCoder Beginner Contest 068
数字に変換後、「.」で数字を出力してioの「nl」で改行と無駄なことをしていますね。
ABC244 A問題
A - Last Letter
文字数Nと文字列が与えられるのでN文字目を出力する問題です。
USING: io sequences kernel math math.parser strings ;
readln string>number ! 5
readln ! 5 "abcde"
swap ! "abcde" 5
1 - ! "abcde" 4
swap ! 4 "abcde"
nth ! 101 ('e')
1string write ! e
! write1
nth でN-1番目のcharをとってきて出力します。とってきたcharは数字になっているのでstringsの「1string」でstrに変換、もしくはioの「write1」で出力する必要があります。なおNを受け取った時点で1を引いておけばswapの必要はありません。
Submission #53728872 - AtCoder Beginner Contest 244
一行で複数受け取る
このままでは以下のような入力が受け取れない!とお怒りでしょう。
N
A1 A2 . . . AN
こんな時に使うのがsplittingの「split」です。「readln」で受け取ってセパレータで指定した文字で区切るとstrのarrayが返ってきます。
公式の実行例より引用:「" "」と「"-"」を指定
USING: prettyprint splitting ;
"hello world-how are you?" " -" split . ! { "hello" "world" "how" "are" "you?" }
split ( seq separators -- pieces ) - Factor Documentation
sequencesの 「[ ] each」で 「[ ]」内に行いたい操作を書くと配列の要素全てに同じ操作を適用できます。 配列に全て同じ操作をしてまたスタックに返して欲しい時はsequencesの「[ ] map」です。「[ ] 」は無名関数・ラムダ式に相当する処理でquotationを表す構文です。「map」や「each」はquotationを受け取るように定義されているため、「[ ] 」で囲んであげる必要があります。
USING: io kernel prettyprint splitting sequences ;
readln ! "How Are You?"
" " split ! { "How" "Are" "You?" }
[ "@" append ] map ! { "How@" "Are@" "You?@" }
[ write "_" write ] each ! How@_Are@_You?@_
数字の配列として受け取りたいときはstrのarrayで受け取った後に、「[ ] map」で「string>number」を適用すれば良いです。ちょっとPythonっぽいですね。
readln " " split [ string>number ] map
とりあえずこれらだけでも色々AC出来るようになりました。
ABC169 A問題
A - Multiplication 1
整数を二つ受け取ってその積を出力するだけです。「first2」で配列から先頭二つの要素が先頭から順にスタックに返されるので、「*」してあげれば良いです。
USING: io splitting sequences math.parser math prettyprint ; readln " " split [ string>number ] map first2 * . nl
Submission #53704222 - AtCoder Beginner Contest 169
条件分岐・ループ処理・変数・関数
if文
Factorでの条件分岐、if文です。kernelにあります。
if ( ..a ? true: ( ..a -- ..b ) false: ( ..a -- ..b ) -- ..b ) - Factor Documentation
boolean型を受け取って最初のquatation 「[ ] 」内にtrueの時の処理を、「[ ]」内にfalseの時の処理を書きます。
ABC181 A問題
A - Heavy Rotation
奇数ならBlack、偶数ならWhiteを出力するだけです。
USING: io kernel math math.parser ; readln string>number ! Nを受け取る 2 mod ! 2で割ったあまり 1 = ! 1 と等しいか [ "Black" print ] ! trueの場合 [ "White" print ] ! falseの場合 if ! t/f [ ] [ ] if
Submission #53727579 - AtCoder Beginner Contest 181
loop処理
ループ処理です。こちらもkernelです。
loop ( ... pred: ( ... -- ... ? ) -- ... ) - Factor Documentation
quotation「[ ]」でfが返されるまで、quotation内の処理をループします。
公式の実行例より引用:
USING: kernel prettyprint math ; 3 [ dup . 7 + 11 mod dup 3 = not ] loop drop
3から始めて+7 (mod 11) をし続け、再び3に戻るまでのループ処理です。 「not」はfの場合tを出力しますので、3の時のみfになります。
3
10
6
2
9
5
1
8
4
0
7
回数指定はmathの「times」です。回数と quotation「[ ]」を入力します。
times ( ... n quot: ( ... -- ... ) -- ... ) - Factor Documentation
公式の実行例より引用:
USING: io math ; 3 [ "Hi" print ] times
Hi
Hi
Hi
ABC333 A問題
A - Three Threes
整数N(1≦N≦9)を受け取ってNをN回繋げて出力する問題です。ループの基本問題です。
USING: io kernel math math.parser ; readln string>number ! 5 dup ! 5 5 number>string ! 5 "5" swap ! "5" 5 : 書く文字とカウンタを用意 [ over ! "5" 5 "5" : "5"を複製 write ! "5" 5 : 5を書く 1 - ! "5" 4 : カウンタを1減らす dup ! "5" 4 4 : 比較用にカウンタを複製 0 > ! "5" 4 t : カウンタが>0の間次のループへ ] loop ! "5" 0 : fが返ってきたら抜ける 2drop ! : スタックを空にする nl ! : 改行を出力
Submission #61311375 - Toyota Programming Contest 2023#8(AtCoder Beginner Contest 333)
timesを使えば楽でした。
変数
簡単なプログラムならば変数はスタックに積んでおけば良いのですが、宣言も出来ます。SYMBOLという名前になっています。
Dynamic variables cookbook - Factor Documentation
namespacesをUSINGすると使える「set」「get」がsetter、getterになっています。IN: で適当なvocabulary名をつけておく必要があります。
USING: io kernel math math.parser namespaces prettyprint ; IN: rrrriki SYMBOL: WALLET 40000 ! 40000 WALLET set ! : WALLET に40000を代入 WALLET get ! 40000 : WALLET から40000をスタックに積む 36000 - ! 4000 WALLET set ! : WALLET に4000を代入 7000 ! 7000 WALLET get ! 7000 4000 : WALLET から4000をスタックに積む - . ! 3000
word(関数)を定義
main関数に全処理を書きたくなければ定義することになります。同じようにIN: で適当なvocabulary名をつけておいて属させる必要があります。
以下の様にwordを定義します
: word名 ( 入力 -- 出力 ) 操作 ;
Vocabularies cookbook - Factor Documentation
例えば二乗した値を返すsqはmathで以下の様に定義されています。
USING: kernel ; IN: math : sq ( x -- y ) dup * ; inline
sq ( x -- y ) - Factor Documentation
自身を複製してx * xの値を返しています。xとは異なるので出力はyと区別して書かれています。ここの入出力の書き方は既存のwordを参考にします。
ABC162 B問題
B - FizzBuzz Sum
1からNまでの3の倍数でも5の倍数でもない整数の合計を出力する問題です。
3の倍数か5の倍数の場合にtを返すような関数isFizzBuzzを用意して判定し、変数SUMに合計を格納してみます。
USING: io kernel math math.parser namespaces prettyprint ; IN: rrrriki : isFizzBuzz ( x -- ? ) ! 値を受け取って3もしくは5の倍数の時tを返す dup ! 二回比較用に複製 3 mod 0 = ! 3の倍数かどうか swap ! 結果を下に数字を上に 5 mod 0 = ! 5の倍数かどうか or ; ! orをとる ! 変数SUMを宣言 SYMBOL: SUM 0 SUM set ! SUMに0をセット readln string>number ! 15 [ dup ! 15 15 isFizzBuzz ! 15 t [ ] ! t で何もしない [ dup SUM get + SUM set ] ! f でSUMに加算 if ! t/f [ ] [ ] if 1 - ! 14 dup ! 14 14 : 比較用に複製 0 > ! 14 t : >0の間次のループへ ] loop ! 0 f : fが返ってきたら抜ける drop SUM get . ! : 0を捨ててSUMを出力
Submission #61313701 - AtCoder Beginner Contest 162
文字列の処理、検索
arrayにしないと上手くいきませんでした。正しい方法があれば教えて下さい。
USING: prettyprint sequences kernel arrays ;
"Hello" >array ! { 72 101 108 108 111 }
"e" ! { 72 101 108 108 111 } "e"
first ! { 72 101 108 108 111 } 101
swap ! 101 { 72 101 108 108 111 }
index . ! 1
AtCoder Beginners Selectionを解く
終わりに
一番簡単にLanguage Ownersの1stがとれそうな言語として、2024年5月20-22日にAtCoder NoviStepsの簡単な方から埋めてたのですが、正月勉強のやる気が起きないので記憶から起こしておきました。
よく分からなさすぎたので当初はこちらを見ながら勉強していました。もっとまともな事が書かれていて本質的です。
mopemope.hatenablog.com当初は無かった色々複雑そうなQiitaの記事が生えていたので列挙しておきます。
string>numberとnumber>stringを使わないとこうなるらしいです。難しいですね。
qiita.com string>numberがあると足すだけになります。
Submission #53704921 - AtCoder Beginners Selection
終わりに2
もし試した方がいたら提出言語の変更には気をつけて下さい。
*1:strで上手く「index」で文字を検索する方法が分からずarrayに変換してました、文字列の検索参照