新人报到,写了个中文词性高亮的小功能,请各位前辈批评

哈哈哈哈谢谢!我大emacs怎么可能比不过这些写作软件 :smile:

:joy:11月30日更新:

  1. 修复按词移动在句首的失败的问题
  2. 修复输入文字后快速输入标点或换行后上一句未高亮的问题
  3. 玩具式地应用了一下defstruct,路漫漫其修远兮

这次可是暴露出我很多考虑不周的问题来了,给大家道个歉,以后一定好好检查代码哈哈哈哈

感谢 @LdBeth 前辈的指导!希望没有辜负各位前辈的指点 :see_no_evil:

(defgroup cnhl-colors nil
  "中文词性颜色"
  :group 'cnhl-color)

;; 类比色
;; 红色系
(defface cnhl-n-r-j
  '((t (:foreground "#F0B0A5")))
  "名词、代词、简称颜色"
  :group 'cnhl-colors)
(defface cnhl-np
  '((t (:foreground "#F0A299")))
  "人名颜色"
  :group 'cnhl-colors)
;; 绿色系
(defface cnhl-v-i-id
  '((t (:foreground "#C8E3A1")))
  "动词、习语颜色"
  :group 'cnhl-colors)
;; 橙黄色系
(defface cnhl-a
  '((t (:foreground "#EDC7A8")))
  "形容词颜色"
  :group 'cnhl-colors)
(defface cnhl-f-s-t-m-q-mq
  '((t (:foreground "#EDBD9D")))
  "方位词、处所词、时间词、数词、量词、数量词颜色"
  :group 'cnhl-colors)
;; 浅黄色系
(defface cnhl-d
  '((t (:foreground "#EDE7A8")))
  "副词颜色"
  :group 'cnhl-colors)
(defface cnhl-c
  '((t (:foreground "#EDE39D")))
  "连词颜色"
  :group 'cnhl-colors)
(defface cnhl-p
  '((t (:foreground "#EDE891")))
  "介词颜色"
  :group 'cnhl-colors)
;; 中黄色系
(defface cnhl-u-y-h-k
  '((t (:foreground "#D7C8A4")))
  "助词、语气助词、前接成分、后接成分颜色"
  :group 'cnhl-colors)
(defface cnhl-e-o
  '((t (:foreground "#D6C299")))
  "叹词、拟声词颜色"
  :group 'cnhl-colors)
;; 其它
(defface cnhl-g-w-x
  '((t (:foreground "gray80")))
  "语素、标点及其它颜色"
  :group 'cnhl-colors)

;; 浓度色
(defface cnhl-shades-n-r-j
  '((t (:foreground "#FFD6CC")))
  "名词、代词、简称颜色"
  :group 'cnhl-colors)
(defface cnhl-shades-v-i-id
  '((t (:foreground "#FFECC2")))
  "动词、习语颜色"
  :group 'cnhl-colors)
(defface cnhl-shades-a-f-s-t-m-q-mq
  '((t (:foreground "#CCBC9B")))
  "形容词、方位词、处所词、时间词、数词、量词、数量词颜色"
  :group 'cnhl-colors)
(defface cnhl-shades-d-c-p
  '((t (:foreground "#C0A199")))
  "副词、连词、介词颜色"
  :group 'cnhl-colors)
(defface cnhl-shades-u-y-h-k-e-o
  '((t (:foreground "#998D74")))
  "助词、语气助词、前接成分、后接成分、叹词、拟声词颜色"
  :group 'cnhl-colors)
(defface cnhl-shades-g-w-x
  '((t (:foreground "gray65")))
  "语素、标点及其它颜色"
  :group 'cnhl-colors)

;; 简化浓度色
(defface cnhl-shades-simple-n-r-j-v-i-d
  '((t (:foreground "#FFECC2")))
  "名词、代词、简称、动词、习语颜色"
  :group 'cnhl-colors)
(defface cnhl-shades-simple-a-f-s-t-m-q-mq-d-c-p
  '((t (:foreground "#DFD1B1")))
  "形容词、方位词、处所词、时间词、数词、量词、数量词、副词、连词、介词颜色"
  :group 'cnhl-colors)
(defface cnhl-shades-simple-u-y-h-k-e-o
  '((t (:foreground "#A99D84")))
  "助词、语气助词、前接成分、后接成分、叹词、拟声词颜色"
  :group 'cnhl-colors)

(defvar *cnhl-color-scheme* nil
  "cnhl中文词性高亮配色方案
nil代表类比色(色彩最丰富),simple代表简化浓度色(色彩最简单),其余值代表浓度色(红黄色系五色)")

(defun thulac-start ()
  "启动python版thulac词性分析process"
  (make-process
   :name "thulac"
   :buffer "*thulac*"
   :command (list "python3" "-c" "import sys;import json;import thulac;t=thulac.thulac();exec('while True:print(json.dumps(t.cut(sys.stdin.readline()),ensure_ascii=False))')"))
  ;; 第一次实例化thulac时会耗费一段时间加载分析模型,加载完毕会输出"Model loaded succeed"作为提示
  ;; 使用accept-process-output等待加载完成。这段时间(大约数秒)emacs会卡住。
  (accept-process-output (get-process "thulac"))
  ;; 绑定filter
  (set-process-filter
   (get-process "thulac")
   'thulac-filter)
  ;; 退出emacs时自动杀掉thulac process
  (set-process-query-on-exit-flag (get-process "thulac") nil))

(defvar *cnhl-hl-enable* t
  "是否启用高亮,用于仅分词的情况")

(defvar *cnhl-last-info*
  (list :beg nil :end nil :result nil :buffer nil)
  "上一分析句的信息")

(defvar *cnhl-filter-timer* nil
  "用于临时存储filter所用的timer")

(defvar *cnhl-major-mode* (list 'org-journal-mode 'org-mode 'text-mode)
  "允许进行中文高亮的buffer mode")

(defvar *cnhl-whole-buffer* nil
  "全buffer高亮模式开关变量")

(defun thulac-filter (process output)
  "接收python thulac传回的中文分析结果,进行初步处理后执行操作函数"
  (when (member major-mode *cnhl-major-mode*)
    ;; 使用python -c方式运行的process,单次输出较长的字符串时会被emacs分段读取传入filter(大约在第800C左右),
    ;; 执行python文件则无此问题。于是此处设置timer延迟执行,防止意外断句
    ;; 如果有正在准备执行的操作就干掉它重来
    (when *cnhl-filter-timer*
      (cancel-timer *cnhl-filter-timer*)
      (setq *cnhl-filter-timer* nil)
      ;; 把新到的输出加进记录中
      (setq output
            (concat (plist-get *cnhl-last-info* :result)
                    output)))
    ;; 把输出记录下来
    (plist-put *cnhl-last-info* :result output)
    ;; 设置timer
    (setq *cnhl-filter-timer*
          (run-at-time
           ;; 默认间隔0.05s足矣
           "0.05" nil '(lambda ()
                         ;; 若开启高亮则执行
                         (when *cnhl-hl-enable*
                           (cnhl-run))
                         ;; 执行高亮后删掉自己
                         (setq *cnhl-filter-timer* nil)
                         ;; 若启动全buffer高亮,则自动跳到下一行重复执行高亮
                         (when *cnhl-whole-buffer*
                           (goto-line (+ (line-number-at-pos) 1))
                           (cnhl)
                           ;; 执行到buffer底部则退出全buffer高亮
                           (when (= (line-end-position) (buffer-end 1))
                             (setq *cnhl-whole-buffer* nil))))))))

(cl-defstruct (cnhl-word (:type vector)) word part)

(defun cnhl-current-word-info ()
  "获取光标所在词的一系列信息,用于中文分词"
  (interactive)
  ;; 若缓存中没有光标所在句的结果则对当前句执行高亮以获得信息
  (unless (and (plist-get *cnhl-last-info* :result)
               (string= (buffer-name) (plist-get *cnhl-last-info* :buffer))
               (> (point) (plist-get *cnhl-last-info* :beg))
               (< (point) (plist-get *cnhl-last-info* :end)))
    (cnhl-sentence)
    ;; 等待分析结果出来,避免因为timer延时而导致的意外
    (accept-process-output (get-process "thulac")))
  (block body
    ;; 从前往后将每个词的结束位置与光标位置相比较,当某词词末大于光标位置时,该位置即为光标所在词词末
    ;; array - 句子分析结果;p - 光标词词末;prev-1 - 上一词词末;prev-2 - 上上词词末
    (let ((array (json-parse-string
                  (plist-get *cnhl-last-info* :result)))
          (p (plist-get *cnhl-last-info* :beg))
          (prev-1 nil)
          (prev-2 nil))
      ;; idx - 在遍历的词的位置;word - 在遍历的词
      (do* ((idx 0 (1+ idx))
            (word (cnhl-word-word (aref array idx)) (cnhl-word-word (aref array idx))))
          ((< idx 0))
        ;; 挨个腾地儿
        (setq prev-2 prev-1)
        (setq prev-1 p)
        (setq p (+ p (length word)))
        ;; 当正在遍历的词末位置大于光标位置时
        (when (> p (point))
          ;; 返回本次词末,上词词末,上上词词末
          (return-from body (list :end p :prev-1 prev-1 :prev-2 prev-2)))))))

(defun cnhl-forward-word ()
  (interactive)
  (if (member major-mode *cnhl-major-mode*)
      ;; 前往本词词末,即下一词词首
      (goto-char (plist-get (cnhl-current-word-info) :end))
    ;; 若不在设置启用的major-mode中,则fallback回emacs默认的分词操作函数
    (forward-word)))

(defun cnhl-backward-word ()
  (interactive)
  (if (member major-mode *cnhl-major-mode*)
      ;; 光标在句首时,无法抓到上一句的标点,手动将光标前移
      (if (= (point) (line-beginning-position))
          (goto-char (- (point) 1))
        ;; 前往上上词词末,即上词词首
        (goto-char (plist-get (cnhl-current-word-info) :prev-2)))
    (backward-word)))

(defun cnhl-backward-kill-word ()
  (interactive)
  (if (member major-mode *cnhl-major-mode*)
      (progn (when (= (point) (line-beginning-position))
               (goto-char (- (point) 1)))
             (let ((result (cnhl-current-word-info)))
               ;; 如果光标位置在词末,删掉上上词词末至上词词末,即上词词首至本词词首,即上一个词
               (if (= (point) (plist-get result :prev-1))
                   (delete-region
                    (plist-get result :prev-2)
                    (plist-get result :prev-1))
                 ;; 否则整个删掉光标所在词
                 (delete-region
                  (plist-get result :prev-1)
                  (plist-get result :end)))))
    (backward-kill-word 1)))

(defun cnhl-kill-word ()
  (interactive)
  (if (member major-mode *cnhl-major-mode*)
      (let ((result (cnhl-current-word-info)))
        ;; 删掉上词词末至本词词末,即本词词首至下词词首,即本词
        (delete-region
         (plist-get result :prev-1)
         (plist-get result :end)))
    (kill-word 1)))


(defun cnhl-run ()
  "根据存储的信息执行中文词性高亮"
  (save-excursion
    ;; 清除高亮区域内已有的overlay
    (dolist (overlay (overlays-in (plist-get *cnhl-last-info* :beg)
                                  (plist-get *cnhl-last-info* :end)))
      (delete-overlay overlay))
    ;; 光标移到句首
    (goto-char (plist-get *cnhl-last-info* :beg))
    ;; 设变量num,从0开始遍历所有分析出的词语
    (let ((num 0)
          (output (json-parse-string (plist-get *cnhl-last-info* :result))))
      (while num
        (condition-case err
            ;; 选取item为output数组中的第num个词的分析结果,格式为数组,第0项为词语,第1项为词性标识
            (let* ((item (aref output num))
                   ;; 向前搜索item的第一项,即目标词语,记录词语后端位置,搜索范围仅限分析句内
                   (end (search-forward
                         (cnhl-word-word item)
                         (plist-get *cnhl-last-info* :end)))
                   ;; 根据搜索出的位置新建overlay
                   (overlay (make-overlay
                             (- end (length (aref item 0))) end)))
              ;; 贴overlay的颜色
              (overlay-put
               overlay 'face
               (if *cnhl-color-scheme*
                   (if (string= *cnhl-color-scheme* "simple")
                       ;; 通过语法标识判断应选用的颜色
                       ;; 简单浓度色(黄色系三色)
                       (pcase (cnhl-word-part item)
                         ((or "n" "ns" "ni" "nz" "r" "j" "np" "v" "i" "d")
                          'cnhl-shades-simple-n-r-j-v-i-d)
                         ((or "a" "f" "s" "t" "m" "q" "mq" "d" "c" "p")
                          'cnhl-shades-simple-a-f-s-t-m-q-mq-d-c-p)
                         ((or "u" "y" "h" "k" "e" "o")
                          'cnhl-shades-simple-u-y-h-k-e-o)
                         (_
                          'cnhl-shades-g-w-x))
                     ;; 浓度色(红黄色系五色)
                     (pcase (cnhl-word-part item)
                       ((or "n" "ns" "ni" "nz" "r" "j" "np")
                        'cnhl-shades-n-r-j)
                       ((or "u" "y" "h" "k" "e" "o")
                        'cnhl-shades-u-y-h-k-e-o)
                       ((or "v" "id" "i")
                        'cnhl-shades-v-i-id)
                       ((or "d" "c" "p")
                        'cnhl-shades-d-c-p)
                       ((or "a" "f" "s" "t" "m" "q" "mq")
                        'cnhl-shades-a-f-s-t-m-q-mq)
                       (_
                        'cnhl-shades-g-w-x)))
                 ;; 类比色(彩色)
                 (pcase (cnhl-word-part item)
                   ((or "n" "ns" "ni" "nz" "r" "j")
                    'cnhl-n-r-j)
                   ((or "u" "y" "h" "k")
                    'cnhl-u-y-h-k)
                   ((or "v" "id" "i")
                    'cnhl-v-i-id)
                   ((or "np")
                    'cnhl-np)
                   ((or "a")
                    'cnhl-a)
                   ((or "p")
                    'cnhl-p)
                   ((or "d")
                    'cnhl-d)
                   ((or "c")
                    'cnhl-c)
                   ((or "f" "s" "t" "m" "q" "mq")
                    'cnhl-f-s-t-m-q-mq)
                   ((or "e" "o")
                    'cnhl-e-o)
                   (_
                    'cnhl-g-w-x))))
              ;; 贴完了,num加一,开始处理下一个词
              (setq num (+ num 1)))
          ;; 如果报错(如搜索出界),则停止当前句的高亮过程
          (t (setq num nil)))))))

(defun cnhl (&optional beg end)
  "中文词性高亮"
  (interactive)
  ;; 清除可能意外存留的filter timer,防止因高亮过程意外终止,timer未正常清除导致的高亮失败
  (setq *cnhl-filter-timer* nil)
  ;; 启动thulac
  (unless (get-process "thulac") (thulac-start))
  ;; 若无参数运行,则默认取当前行
  (unless beg
    (setq beg (line-beginning-position))
    (setq end (line-end-position)))
  ;; 记录字符串数据
  (setq *cnhl-last-info*
        (list :beg beg
              :end end
              :buffer (buffer-name)))
  ;; 将范围内的字符串送入thulac进行分析,结果返回至thulac-receive-output进行操作
  (process-send-string
   "thulac"
   (concat
    (string-trim (buffer-substring-no-properties beg end))
    "\n")))

(defun cnhl-buffer ()
  "对当前buffer全文逐行进行中文词性高亮"
  (interactive)
  ;; 启动全文高亮后移动光标至顶部,开始进行高亮
  (setq *cnhl-whole-buffer* t)
  (goto-char (point-min))
  (cnhl))

(defun cnhl-sentence ()
  "对当前句进行中文词性高亮"
  (interactive)
  (save-excursion
    ;; 生成句读匹配的正则
    (let* ((regxp (rx (or (group ",")
                          (group "。")
                          (group "?")
                          (group ";")
                          (group ":")
                          (group "、")
                          (group "‘")
                          (group "’")
                          (group "“")
                          (group "”")
                          (group "…")
                          (group "!")
                          (group "(")
                          (group ")")
                          (group "~")
                          (group "*"))))
           (end
            ;; 语段起始结束位置,若无标点则直到行末
            (save-excursion
              (or (search-forward-regexp regxp (line-end-position) t)
                  (line-end-position))))
           (beg
            ;; 本次编辑的起始点小于光标位置,则从起始点为高亮起点向前搜索
            (progn
              (when (and *cnhl-sentence-beginning-position*
                         (< *cnhl-sentence-beginning-position* (point)))
                (goto-char *cnhl-sentence-beginning-position*)
                (setq *cnhl-sentence-beginning-position* nil))
              (or (search-backward-regexp regxp (line-beginning-position) t)
                  (line-beginning-position)))))
      ;; 对语段进行中文高亮
      (cnhl beg end))))

(defvar *cnhl-sentence-timer* nil
  "用于临时存储cnhl实时高亮的timer")

(defvar *cnhl-sentence-beginning-position* nil
  "记录本此语句高亮的起始点")

(defun cnhl-sentence-timely (beg end len)
  "中文实时高亮函数,加入after-change-functions以实现实时高亮输入内容"
  (when (member major-mode *cnhl-major-mode*)
    (if *cnhl-sentence-timer*
        ;; 删除准备执行的高亮
        (cancel-timer *cnhl-sentence-timer*)
      ;; 记录起始点
      (setq *cnhl-sentence-beginning-position* (point)))
    ;; 安排高亮任务,延迟一段时间后执行,防止过于频繁地执行高亮造成资源浪费
    (setq *cnhl-sentence-timer*
          (run-at-time
           ;; 默认延迟0.1秒
           "0.1" nil '(lambda ()
                        (cnhl-sentence)
                        ;; 执行完删除timer
                        (setq *cnhl-sentence-timer* nil))))))

(defun cnhl-switch-color ()
  "切换中文高亮配色方案"
  (interactive)
  ;; 按照 类比 - 浓度 - 简化浓度 的顺序切换配色方案
  (if *cnhl-color-scheme*
      (if (string= *cnhl-color-scheme* "simple")
          (setq *cnhl-color-scheme* nil)
        (setq *cnhl-color-scheme* "simple"))
    (setq *cnhl-color-scheme* ""))
  ;; 切换后对当前行重新高亮
  (cnhl))

;; cnhl中文分词操作按键绑定
(global-set-key (kbd "M-f") 'cnhl-forward-word)
(global-set-key (kbd "M-b") 'cnhl-backward-word)
(global-set-key (kbd "M-d") 'cnhl-kill-word)
(global-set-key (kbd "M-DEL") 'cnhl-backward-kill-word)

;; 启用实时高亮输入内容
(add-hook 'after-change-functions #'cnhl-sentence-timely)

1

增加一个命令参数变量比较好:

(defvar thulac-start-command (list "python3" "-c" "import sys;import json;import thulac;t=thulac.thulac();exec('while True:print(json.dumps(t.cut(sys.stdin.readline()),ensure_ascii=False))')"))

...

   :command thulac-start-command

2

可改为:


(defvar cnhl-word-simple-face-alist
  '((("n" "ns" "ni" "nz" "r" "j" "np" "v" "i" "d") . cnhl-shades-simple-n-r-j-v-i-d)
    (("a" "f" "s" "t" "m" "q" "mq" "d" "c" "p") . cnhl-shades-simple-a-f-s-t-m-q-mq-d-c-p)
    (("u" "y" "h" "k" "e" "o") . cnhl-shades-simple-u-y-h-k-e-o)))

...

               (if *cnhl-color-scheme*
                   (if (string= *cnhl-color-scheme* "simple")
                       ;; 通过语法标识判断应选用的颜色
                       ;; 简单浓度色(黄色系三色)
                       (or (cl-assoc (cnhl-word-part item) cnhl-word-simple-face-alist :test #'member)
                           'cnhl-shades-g-w-x)

其余两种配色类似。

3

是不是可以改为?

(let* ((regexp (rx (group (or "," "。" "?" ";" ":" "、" "‘" "’" "“" "”" "…" "!" "(" ")" "~" "*")))))

4

如果想发布到 melpa,变量和函数的命名必需统一。

谢谢前辈指点!我会及时修改! :laughing: :laughing:

年纪轻轻,好学,有才,还这么谦虚,真是前途无量啊

很抱歉,前段时间为了准备合格考和期末考试,暂时放下了这边的完善工作,有些辜负大家的指教。现在放假了,我又来交作业啦!

虽然我也没想过要提交到melpa什么的(水平差远了哈哈),但每次都一股脑儿把所有代码扔进论坛也不甚雅观。于是我将这个小插件正式定名为 cnhl 放到了github上,亦是方便大家安装使用(感谢 @twlz0ne 前辈的提醒!)。


这次在已有代码上依然有比较多的改动,请各位前辈指导:

  1. 弄了个不三不四的“trie”出来(也不知道算不算trie :joy: 再次感谢 @LdBeth 前辈的指导!),再结合了其它一些不甚雅观的方法改善了一下匹配face部分的代码。

    具体思路是:预置一个列表(cnhl--face-items),表中存有thulac返回结果的首字母与face的对应关系,再用这个列表生成一个哈希表(不知道怎么初始化哈希表的值,只好用loop的笨方法挨个写进去……),然后每次使用thulac返回值的首字母字母去匹配哈希表中的值。这部分代码如下:

    不知道有没有更好的方法,还请前辈们指教。

  2. 重新理了理timer,解决了上一个版本中一直困扰我的几个大bug,例如快速换行导致高亮失败、删字导致下一句第一个词被清除高亮一类的破问题。现在它在写作中的表现已经相当不错。唯一的问题是一口气高亮超过82个字符的行会失败,算是个重大bug,我仔细研究研究是怎么回事(逻辑苦手当场暴毙qaq)……

  3. 主题上做了很大的修改。根据一段时间的使用经验和对词语出现概率的估算,我调整了一下对thulac结果的合并方法,让它用起来更清晰舒服;同时去掉了那两个高度合并的方案,只留下一个,因为根据我的写作实际,彩虹糖似的语法高亮并不会对我造成干扰。我还加入了适配亮色主题的颜色(两种,在README中)。

    我的审美不适合所有人,要定制的话,Adobe Color可能能帮到大家——毕竟文字的高亮还是和代码高亮不太一样,既要考虑到清晰度,也要考虑到整体的和谐,不刺眼,不喧宾夺主,保证思路连贯。

    直接效果就是代码量小了好多哈哈,看着清楚多了!有机会把设计思路总结总结。

  4. 我现在在用emacs29,在29中,原先的return函数被cl-return所替换,我不知道它能不能向下兼容。如果cnhl在旧版本上报错的话,我会新建一个分支。 感谢 @twlz0ne 前辈!不存在问题,是我想当然了qwq

  5. 一些细节上的优化,像语法优化、cl-loop的使用、封装了一个开关高亮的函数之类的杂七杂八的东西,倒是为自己微不足道的进步沾沾自喜了一阵子。另外我还学着其它包给cnhl.el装模作样地加了个头尾,不知道符不符合规范,还请前辈们批评。

看上去是能用了,但我知道它还是过于简陋,连minor mode都没做。下一步我打算让它更像样些,加入一些必要的功能;如果有可能还想换用c++版的thulac,这样就不需要折腾一堆python了,或者直接写成dynamic module——但那离我还太远了,好高骛远要不得。要学习的地方很多呀。

另外还有部分细节体验较差,例如在org有序列表的一行中按下M-RET会导致当前行的高亮被删除;使用一些生成文本的功能后会报找不到下文的错误;高亮范围有时会越出标点导致性能浪费等,我会着手修复。

哦对了,我还想写一篇对文科生们的emacs安利文,不知道有没有哪位前辈已经写过了,我继续补充发扬(flag立在这儿了awa)。


感谢一路指导我的各位前辈们,小子无以为报,旧作一片以酬诸君:

莫疑繁此为何妨,夜觅阑珊守尺方。

代码行行映人肃,个中点点入心长。

肠肥恶见三牲貌,腹馁唯知一口张。

当敬人间才与苦,方成诸夏景还长。

还望各位前辈多多批评!

4 个赞

感谢前辈!第三条建议确实是不行的,那样生成的正则遇到行尾可能会出现一些问题,会导致报错……其它地方我都做出了修改 :laughing:

不存在替换的问题。cl-return 一直都在 cl-macs 文件中,以前使用 return 是因为那时大家都习惯 (require 'cl) 这样大包大揽的导入方式,现在已经不推荐了。

1 个赞

我使用的是python 3.10, 我根据您文档中的建议将CBTaggingDecoder.py中的start = time.start()改为start = time.pref_counter()。 但是并不能工作,将其改为time.perf_counter()后方可运行,您可能写错了。

2 个赞

感谢你的指正!很高兴看到有人使用我写的小插件!

这确实是我的巨大疏忽,先前我还遇到过这个问题,转眼就给忘了🤣

README已修改,祝你用得开心,我会继续努力的~

(其实据说可以直接删掉那一行……我是没试过啦哈哈)

2 个赞

好像是的,后面的代码没有用这个变量。

我刚刚测试了,删掉也可以用。

使用时message buffer中出现大量的 Invalid face reference: quote [770 times]

哇,谢谢你愿意使用它!当初初学elisp编程,写的代码还是太稚嫩了些,很抱歉给你造成了麻烦quq

我在使用的时候并没有遇到这样的问题,不过我猜测问题大概发生在操作overlay的部分。不仅这里,整个cnhl中大大小小的问题也太多了,如果你不介意的话,我这段日子再重写一版,把一些必要的功能加进去,重写完后再通知你ww

蓬山此去无多路,青鸟殷勤为探看 :partying_face:

1 个赞

很喜欢你的包,写笔记的时候可以中文分词了。 这里找到一个新 bug:

  + 在明确评价机制的基础上形成伺服系统的指标要求,据此找厂商定做伺服系统。

上面这行文本(注意前面有两个空格),在使用cnhl-forward-word将光标移动到中间的逗号以后,出现如下错误

Debugger entered--Lisp error: (args-out-of-range [["+" "w"] [" " "g"] ["明确" "a"] ["评" "v"] ["价" "n"] ["机制" "n"] ["的" "u"] ["基础" "n"] ["上" "f"] ["形成" "v"] ["伺服" "v"] ["系统" "a"] ["的" "u"] ["指标" "n"] ["要求" "n"] ["," "w"] ["\n" ""]] 17)
  cnhl--current-word-info()
  cnhl-forward-word()
  #<subr funcall-interactively>(cnhl-forward-word)
  apply(#<subr funcall-interactively> cnhl-forward-word)
  funcall-interactively(cnhl-forward-word)
  #<subr call-interactively>(cnhl-forward-word nil nil)
  apply(#<subr call-interactively> (cnhl-forward-word nil nil))
  explain-pause--wrap-call-interactively(#<subr call-interactively> cnhl-forward-word nil nil)
  apply(explain-pause--wrap-call-interactively #<subr call-interactively> (cnhl-forward-word nil nil))
  call-interactively(cnhl-forward-word nil nil)
  command-execute(cnhl-forward-word)
1 个赞

啊,我明白你的意思了!

谢谢你的提醒,我这次会采用新的方式来计算所有的位置信息!

幸与同窗多济路,长歌一奋不孤征 ——我的旧诗 :yum:

2 个赞

后生可畏!

我虽然技术没有楼主那么高,但还是想厚着脸皮提点思路,毕竟语义分词听上去就感觉运行起来应该不会很快:

我们在实际使用的过程中(假设简体中文环境),可能不需要那么具体与准确地对语义进行区分,只要能大概分一下主谓宾就可以了。比如「我其次/喜欢/的/是/历史。」这句话,断成「我/其次喜欢的/是/历史。」,我想对于大多数人就够用了。具体来说,除了表示「目的」这一意义的少数词语、成语,「的」字前边的一般都是定语;「什」很少有单独使用的场景;而「们」字一般都跟人称代词有关……那么如果预先建立一个词库,并对其中的词语如「有的放矢」「实事求是」等高亮,剩下的部分以「的」「是」等关键字断开,是不是就能提高cnhl的运行效率了。同时作为Emacser,我也希望这个项目能以纯elisp的方式实现。

插一句题外话,我姑且作一推断,这几句诗创作的时候没有查韵书。「天」和「添」现代普通话同音,近体诗里却不能通压,他们甚至都不属于同一韵摄。我个人认为学点音韵对于格律是有帮助的,比如「出」字古代也是仄声,严格来说颔联出句也是失黏失对的。

谨代表个人意见。

2 个赞

谢谢!谢谢你的建议!

前辈太谦虚啦,我一个文科生可没多少技术,不过是为了不负大家的一片心意,赶鸭子上架啦哈哈 :laughing:

这个小功能本身依赖于thulac,包括它的算法、程序和模型,所以它的功能自然会受到thulac的限制,这点也特别令我火大……怎么说,果然理论和实践是有区别的。

就像你说的,词语功能分的那么细其实并不是最适合写作的;此外thulac通过现在这种方式调用也有非常多的问题,比如那个莫名其妙的800字一断——其实也是我代码水平太菜qaq。

我原本是打算用dynamic module的——其实c的部分已经写好了 :joy:——但听完你的期待后,我也有点想用elisp实现一个词法分析功能了。我也希望能有更多的elisp项目丰富我们的世界。

简单的搜索与位置判断大概是不行的,结果会很容易出错。我先试着啃一啃thulac的源码,如果做不到的话就去请教一下信竞的朋友,实在做不到再用dynamic module,然后试试训练更简单的模型。谢谢你,谢谢你们! :partying_face:


竟然能在这里遇到懂诗的同学!有朋自远方来!

近冠犹懂习翰墨,寸管悬悬总二更。

一捧沁脾天作韵,不离辗转绕梁声。

未潜书海至终老,敢请文心许此生。

幸与同窗多济路,长歌一奋不孤征。

          (八庚)

:arrow_up:上个回复里引的那句的全文,也是我想送给大家的!

感谢你的指点,长知识了!不过其实我是不抗拒新韵的。我一般是在用平水的时候注明韵部,新韵的时候则不注明,像上面那样,刚好和很多人相反 :joy: 可能是因为我开始写诗时新韵用的比较多吧,或者是虚荣心的原因呢?~

不过关于新韵,其实目前也确实褒贬不一,经常就会听一个教授前脚喷完新韵,另一个教授后脚就说“有些人顽固不化”…… :joy: 诗人真的和程序员一样可爱ww

用结巴分词.

结巴没有词法分析的,而且如果只是分词,结巴和thulac并不会相差太多,thulac官网上有比对数据 :yum:

1 个赞