* simple first emacs script @ 2010-12-15 12:55 Tom 2010-12-15 14:51 ` Pascal J. Bourguignon 0 siblings, 1 reply; 7+ messages in thread From: Tom @ 2010-12-15 12:55 UTC (permalink / raw) To: help-gnu-emacs [-- Attachment #1: Type: text/plain, Size: 3156 bytes --] I am about 2/3 way through the introduction to emacs lisp, and thought it would help my learning if I wrote tried writing a simple script to do something useful to me with the lessons I have covered so far so far. I should point out I am surgeon not a computer scientist and know nothing more about programming than what is contained in the introduction to emacs lisp. I end up with this: (defun nirs-data-clean (number-of-channels) "Cleans the output files from the INVOS monitor removing all columns apart from StO2 values, and the date and time stamps. Sto2 columns to be retained are specified by the NUMBER-OF-CHANNELS variable. It assumes that channels are always used in numerical order, i.e. channel 1 always, then 2, then 3 then 4." (interactive "nEnter number of channels (1-4): ") (barf-if-buffer-read-only) (require 'csv-mode) (push-mark (point-max)) (goto-char (point-min)) (if (or (eq number-of-channels 1) (eq number-of-channels 2) (eq number-of-channels 3) (eq number-of-channels 4)) (or (when (eq number-of-channels 1) (csv-kill-fields '(4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34) (point) (mark))) (when (eq number-of-channels 2) (csv-kill-fields '(4 5 6 7 8 9 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34) (point) (mark))) (when (eq number-of-channels 3) (csv-kill-fields '(4 5 6 7 8 9 11 12 13 14 15 16 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34) (point) (mark))) (when (eq number-of-channels 4) (csv-kill-fields '(4 5 6 7 8 9 11 12 13 14 15 16 18 19 20 21 22 23 25 26 27 28 29 30 31 32 33 34) (point) (mark)) (message "%s is not a valid number of channels. Please enter a number between 1 and 4." number-of-channels)))) (when (y-or-n-p "Replace 0 StO$_2$ values with na: ") (progn (goto-char (point-min)) (while (re-search-forward "[^[:digit:]][0][^[:digit:]]" nil t) (replace-match "na "))))) The idea is to automatically clean unwanted columns from csv (space separated) data file and ask me if I want to replace 0 values with na. I appreciate this is very messy, and I should be able to specify ranges of field rather than listing every single one (but I couldn't get that to work), but it works kind of. When I evaluate it and run it for the first time it works, but if I undo the changes to the file and run it again it often only takes out the 4th column, if I reopen the file or re-evaluate the script it usually works again but I can't seem to work the pattern of working and not working - as I can't see the pattern I can't learn where I am going wrong. I have attached a small sample data file with 4 channels being used to illustrate the kind of file this is supposed to work on. I assume I have made an obvious mistake but I don't have the experience to know what it is. Any general comments about simple/better ways to clean this script would be welcome, for example would I be better making the regexp replace a yes/no arg to the function so if I call this script from another script I can specify a y/n argument (I don't know how to do this anyway though). Tom [-- Attachment #2: 101108N.R14 --] [-- Type: text/plain, Size: 27398 bytes --] 08.11.10 14:57:17 67 0 4 -2.9254 -2.3866 0 0 72 0 4 -3.3003 -2.7971 0 0 63 0 4 -2.8989 -2.2108 0 0 75 0 4 -3.6963 -3.3294 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 14:57:24 69 0 4 -2.9621 -2.4101 0 0 73 0 4 -3.3033 -2.7983 0 0 63 0 4 -2.8936 -2.2066 0 0 75 0 4 -3.7303 -3.3724 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 14:57:30 69 0 4 -2.7329 -2.2557 0 0 74 0 4 -3.3159 -2.8015 0 0 63 0 4 -2.8896 -2.2074 0 0 76 0 4 -3.741 -3.3823 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 14:57:36 71 0 4 -2.9537 -2.4044 0 0 75 0 4 -3.3085 -2.8036 0 0 64 0 4 -2.8861 -2.2033 0 0 72 0 4 -3.676 -3.3543 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 14:57:43 73 0 4 -2.9509 -2.4047 0 0 76 0 4 -3.3151 -2.8131 0 0 65 0 4 -2.889 -2.2082 0 0 74 0 4 -3.7061 -3.3797 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 14:57:49 75 0 4 -2.9304 -2.3887 0 0 76 0 4 -3.3261 -2.8273 0 0 63 0 4 -2.8832 -2.208 0 0 74 0 4 -3.6884 -3.3698 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 14:57:55 74 0 4 -2.9003 -2.3646 0 0 75 0 4 -3.3277 -2.832 0 0 63 0 4 -2.8847 -2.2112 0 0 73 0 4 -3.6876 -3.3781 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 14:58:02 75 0 4 -2.8762 -2.348 0 0 74 0 4 -3.3355 -2.8432 0 0 62 0 4 -2.8635 -2.1954 0 0 72 0 4 -3.6862 -3.3857 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 14:58:08 72 0 4 -2.8286 -2.3068 0 0 74 0 4 -3.3396 -2.8499 0 0 61 0 4 -2.8588 -2.1947 0 0 73 0 4 -3.7164 -3.4112 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 14:58:15 71 0 4 -2.8161 -2.2968 0 0 73 0 4 -3.3412 -2.8519 0 0 59 0 4 -2.861 -2.1985 0 0 74 0 4 -3.7183 -3.4086 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 14:58:21 71 0 4 -2.8015 -2.2887 0 0 72 0 4 -3.338 -2.8518 0 0 58 0 4 -2.8703 -2.206 0 0 74 0 4 -3.7138 -3.4102 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 14:58:27 68 0 4 -2.8104 -2.2935 0 0 71 0 4 -3.3392 -2.8508 0 0 57 0 4 -2.8903 -2.2192 0 0 73 0 4 -3.6995 -3.3984 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 14:58:34 71 0 4 -2.8104 -2.2937 0 0 71 0 4 -3.3386 -2.8442 0 0 57 0 4 -2.9186 -2.2374 0 0 72 0 4 -3.6615 -3.3678 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 14:58:40 70 0 4 -2.817 -2.3025 0 0 72 0 4 -3.3677 -2.868 0 0 59 0 4 -2.9554 -2.2621 0 0 72 0 4 -3.6442 -3.3529 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 14:58:46 70 0 4 -2.8401 -2.3185 0 0 73 0 4 -3.3967 -2.8887 0 0 60 0 4 -2.9572 -2.2624 0 0 72 0 4 -3.6748 -3.3788 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 14:58:53 70 0 4 -2.8539 -2.3277 0 0 74 0 4 -3.4318 -2.9183 0 0 62 0 4 -2.966 -2.2697 0 0 74 0 4 -3.7079 -3.4021 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 14:58:59 73 0 4 -2.8681 -2.342 0 0 75 0 4 -3.4473 -2.934 0 0 64 0 4 -2.979 -2.2834 0 0 74 0 4 -3.7121 -3.4094 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 14:59:05 72 0 4 -2.8718 -2.3429 0 0 75 0 4 -3.4475 -2.9362 0 0 64 0 4 -2.9821 -2.2851 0 0 73 0 4 -3.7008 -3.4025 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 14:59:12 74 0 4 -2.8698 -2.3404 0 0 76 0 4 -3.4461 -2.936 0 0 65 0 4 -2.9665 -2.2722 0 0 73 0 4 -3.7019 -3.4011 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 14:59:18 74 0 4 -2.8557 -2.3283 0 0 77 0 4 -3.4366 -2.9295 0 0 65 0 4 -2.9637 -2.2709 0 0 73 0 4 -3.7009 -3.3997 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 14:59:24 73 0 4 -2.8321 -2.3101 0 0 77 0 4 -3.4126 -2.9116 0 0 66 0 4 -2.9569 -2.2671 0 0 73 0 4 -3.6999 -3.3974 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 14:59:31 73 0 4 -2.8171 -2.2951 0 0 76 0 4 -3.3806 -2.8819 0 0 65 0 4 -2.9406 -2.2551 0 0 72 0 4 -3.678 -3.3855 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 14:59:37 73 0 4 -2.8092 -2.2919 0 0 75 0 4 -3.3859 -2.8922 0 0 64 0 4 -2.9173 -2.2386 0 0 72 0 4 -3.687 -3.3922 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 14:59:43 74 0 4 -2.81 -2.2901 0 0 75 0 4 -3.3926 -2.8987 0 0 64 0 4 -2.9223 -2.2441 0 0 73 0 4 -3.7253 -3.4225 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 14:59:50 72 0 4 -2.7986 -2.2817 0 0 74 0 4 -3.3823 -2.8913 0 0 64 0 4 -2.9115 -2.2381 0 0 73 0 4 -3.6989 -3.4054 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 14:59:56 71 0 4 -2.8065 -2.2867 0 0 73 0 4 -3.387 -2.8981 0 0 63 0 4 -2.9005 -2.2308 0 0 73 0 4 -3.7087 -3.411 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:00:02 71 0 4 -2.8055 -2.2839 0 0 73 0 4 -3.3927 -2.9042 0 0 61 0 4 -2.9139 -2.2439 0 0 74 0 4 -3.7099 -3.412 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:00:09 70 0 4 -2.796 -2.2786 0 0 73 0 4 -3.368 -2.8866 0 0 61 0 4 -2.9006 -2.2343 0 0 73 0 4 -3.6826 -3.3921 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:00:15 70 0 4 -2.7993 -2.2791 0 0 72 0 4 -3.3751 -2.8883 0 0 60 0 4 -2.9171 -2.2468 0 0 73 0 4 -3.6917 -3.3943 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:00:21 69 0 4 -2.8031 -2.2811 0 0 71 0 4 -3.3907 -2.9039 0 0 60 0 4 -2.9495 -2.2707 0 0 74 0 4 -3.6977 -3.3957 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:00:28 70 0 4 -2.8014 -2.2789 0 0 71 0 4 -3.3965 -2.9056 0 0 61 0 4 -2.9823 -2.2931 0 0 73 0 4 -3.6989 -3.4004 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:00:34 69 0 4 -2.7876 -2.2692 0 0 71 0 4 -3.3769 -2.8892 0 0 62 0 4 -2.9697 -2.2819 0 0 74 0 4 -3.7139 -3.4126 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:00:40 69 0 4 -2.7855 -2.2673 0 0 70 0 4 -3.3676 -2.883 0 0 61 0 4 -2.9559 -2.2712 0 0 73 0 4 -3.7021 -3.4029 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:00:47 69 0 4 -2.7921 -2.272 0 0 70 0 4 -3.3812 -2.8929 0 0 61 0 4 -2.9581 -2.2734 0 0 74 0 4 -3.7249 -3.4155 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:00:53 69 0 4 -2.7993 -2.2779 0 0 70 0 4 -3.3923 -2.9034 0 0 61 0 4 -2.9675 -2.2804 0 0 73 0 4 -3.7236 -3.4236 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:01:00 70 0 4 -2.8125 -2.2892 0 0 70 0 4 -3.4108 -2.9178 0 0 62 0 4 -2.9788 -2.2895 0 0 73 0 4 -3.7241 -3.4223 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:01:06 71 0 4 -2.8267 -2.2996 0 0 70 0 4 -3.4234 -2.925 0 0 62 0 4 -2.9952 -2.3017 0 0 74 0 4 -3.722 -3.4167 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:01:12 71 0 4 -2.8285 -2.3022 0 0 71 0 4 -3.4175 -2.9155 0 0 63 0 4 -3.0016 -2.3052 0 0 74 0 4 -3.7148 -3.4108 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:01:18 72 0 4 -2.8268 -2.3017 0 0 72 0 4 -3.4242 -2.9218 0 0 64 0 4 -2.9955 -2.3018 0 0 73 0 4 -3.7007 -3.4105 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:01:25 73 0 4 -2.833 -2.3064 0 0 73 0 4 -3.4451 -2.9422 0 0 65 0 4 -3.0006 -2.3057 0 0 74 0 4 -3.7622 -3.4558 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:01:31 73 0 4 -2.8265 -2.3024 0 0 74 0 4 -3.4433 -2.9401 0 0 65 0 4 -2.9839 -2.2942 0 0 73 0 4 -3.733 -3.4329 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:01:38 73 0 4 -2.8262 -2.3024 0 0 74 0 4 -3.4426 -2.9414 0 0 65 0 4 -2.9788 -2.2908 0 0 73 0 4 -3.7269 -3.4354 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:01:44 74 0 4 -2.8333 -2.308 0 0 75 0 4 -3.4536 -2.95 0 0 66 0 4 -2.993 -2.3017 0 0 73 0 4 -3.7322 -3.4464 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:01:50 74 0 4 -2.8352 -2.3116 0 0 76 0 4 -3.4686 -2.9617 0 0 66 0 4 -2.9907 -2.3032 0 0 73 0 4 -3.7216 -3.4334 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:01:57 76 0 4 -2.867 -2.3394 0 0 76 0 4 -3.517 -2.9986 0 0 66 0 4 -3.0082 -2.3177 0 0 76 0 4 -3.8239 -3.5004 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:02:03 77 0 4 -2.8807 -2.3518 0 0 77 0 4 -3.5242 -3.0046 0 0 66 0 4 -2.997 -2.3068 0 0 75 0 4 -3.805 -3.488 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:02:09 76 0 4 -2.8714 -2.3445 0 0 79 0 4 -3.5094 -2.9927 0 0 66 0 4 -2.9912 -2.3018 0 0 77 0 4 -3.8063 -3.4905 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:02:16 77 0 4 -2.8658 -2.3384 0 0 80 0 4 -3.5121 -2.9965 0 0 66 0 4 -2.9822 -2.2941 0 0 76 0 4 -3.7856 -3.4762 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:02:22 77 0 4 -2.8531 -2.3304 0 0 80 0 4 -3.4822 -2.9729 0 0 66 0 4 -2.9765 -2.2891 0 0 75 0 4 -3.7534 -3.4513 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:02:28 80 0 4 -2.8507 -2.3285 0 0 80 0 4 -3.4815 -2.9765 0 0 65 0 4 -2.9793 -2.2907 0 0 75 0 4 -3.7587 -3.4546 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:02:35 77 0 4 -2.8512 -2.3275 0 0 81 0 4 -3.4948 -2.9887 0 0 65 0 4 -2.9979 -2.307 0 0 74 0 4 -3.7474 -3.4505 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:02:41 78 0 4 -2.8578 -2.3332 0 0 81 0 4 -3.5073 -2.9967 0 0 66 0 4 -3.0193 -2.3256 0 0 75 0 4 -3.7672 -3.4638 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:02:47 78 0 4 -2.8552 -2.332 0 0 80 0 4 -3.4992 -2.9906 0 0 66 0 4 -3.0192 -2.3244 0 0 75 0 4 -3.7629 -3.4565 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:02:54 78 0 4 -2.8521 -2.3279 0 0 80 0 4 -3.4981 -2.9897 0 0 66 0 4 -3.012 -2.3192 0 0 76 0 4 -3.7938 -3.4765 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:03:00 78 0 4 -2.8488 -2.3267 0 0 80 0 4 -3.5088 -3.0015 0 0 66 0 4 -3.0249 -2.3312 0 0 76 0 4 -3.8126 -3.499 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:03:06 77 0 4 -2.839 -2.3185 0 0 80 0 4 -3.5126 -3.0042 0 0 67 0 4 -3.0384 -2.344 0 0 75 0 4 -3.8222 -3.5062 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:03:13 77 0 4 -2.8165 -2.3005 0 0 79 0 4 -3.4722 -2.9619 0 0 67 0 4 -3.0358 -2.3424 0 0 75 0 4 -3.7906 -3.4794 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:03:19 76 0 4 -2.7944 -2.2828 0 0 78 0 4 -3.448 -2.9428 0 0 66 0 4 -3.0146 -2.3256 0 0 74 0 4 -3.7585 -3.457 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:03:25 76 0 4 -2.7678 -2.2613 0 0 77 0 4 -3.4121 -2.913 0 0 66 0 4 -3.006 -2.3196 0 0 74 0 4 -3.7452 -3.4483 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:03:32 75 0 4 -2.749 -2.2503 0 0 76 0 4 -3.3761 -2.8823 0 0 66 0 4 -2.9957 -2.3125 0 0 74 0 4 -3.7516 -3.4504 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:03:38 72 0 4 -2.7313 -2.239 0 0 75 0 4 -3.3506 -2.8579 0 0 65 0 4 -2.9932 -2.3109 0 0 74 0 4 -3.7322 -3.4373 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:03:44 72 0 4 -2.7263 -2.2338 0 0 73 0 4 -3.33 -2.8426 0 0 64 0 4 -2.9748 -2.2976 0 0 73 0 4 -3.6916 -3.4071 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:03:51 71 0 4 -2.7246 -2.2337 0 0 72 0 4 -3.3252 -2.8378 0 0 64 0 4 -2.9822 -2.3028 0 0 73 0 4 -3.711 -3.4185 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:03:57 69 0 4 -2.732 -2.2417 0 0 71 0 4 -3.3147 -2.8289 0 0 63 0 4 -2.9959 -2.3142 0 0 74 0 4 -3.7196 -3.4206 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:04:03 70 0 4 -2.7115 -2.2248 0 0 70 0 4 -3.3041 -2.8196 0 0 62 0 4 -2.9994 -2.3177 0 0 74 0 4 -3.7004 -3.4071 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:04:10 68 0 4 -2.7035 -2.2169 0 0 69 0 4 -3.2991 -2.8146 0 0 63 0 4 -3.0137 -2.3276 0 0 73 0 4 -3.6819 -3.3961 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:04:16 67 0 4 -2.7058 -2.2185 0 0 68 0 4 -3.3001 -2.8124 0 0 63 0 4 -3.0305 -2.3368 0 0 72 0 4 -3.6647 -3.3879 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:04:22 67 0 4 -2.7059 -2.2178 0 0 68 0 4 -3.3032 -2.8135 0 0 63 0 4 -3.0423 -2.3444 0 0 72 0 4 -3.6711 -3.3931 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:04:29 67 0 4 -2.7077 -2.2185 0 0 67 0 4 -3.3035 -2.8148 0 0 64 0 4 -3.0358 -2.3379 0 0 71 0 4 -3.6541 -3.3801 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:04:35 65 0 4 -2.7074 -2.22 0 0 66 0 4 -3.3047 -2.8166 0 0 64 0 4 -3.0249 -2.3291 0 0 71 0 4 -3.6373 -3.3706 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:04:42 66 0 4 -2.7237 -2.232 0 0 66 0 4 -3.323 -2.8318 0 0 64 0 4 -3.022 -2.3288 0 0 71 0 4 -3.6838 -3.4011 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:04:48 66 0 4 -2.7285 -2.2243 0 0 66 0 4 -3.3311 -2.8382 0 0 65 0 4 -3.0328 -2.3339 0 0 76 0 4 -3.9515 -3.6021 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:04:54 68 0 4 -2.7269 -2.2277 0 0 66 0 4 -3.3122 -2.8217 0 0 64 0 4 -3.0169 -2.3215 0 0 76 0 4 -3.8994 -3.5572 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:05:01 67 0 4 -2.7482 -2.2495 0 0 67 0 4 -3.328 -2.8278 0 0 66 0 4 -3.0327 -2.3359 0 0 73 0 4 -3.7202 -3.4457 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:05:07 68 0 4 -2.7579 -2.261 0 0 70 0 4 -3.337 -2.8318 0 0 67 0 4 -3.0739 -2.3621 0 0 71 0 4 -3.6748 -3.417 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:05:13 69 0 4 -2.7848 -2.2801 0 0 73 0 4 -3.3737 -2.8588 0 0 69 0 4 -3.0661 -2.3554 0 0 74 0 4 -3.712 -3.4437 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:05:20 73 0 4 -2.8103 -2.2996 0 0 77 0 4 -3.4046 -2.8849 0 0 70 0 4 -3.0692 -2.3571 0 0 74 0 4 -3.7271 -3.4499 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:05:26 75 0 4 -2.8201 -2.3067 0 0 79 0 4 -3.4242 -2.9043 0 0 70 0 4 -3.0688 -2.3574 0 0 74 0 4 -3.7441 -3.4658 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:05:32 77 0 4 -2.8243 -2.3095 0 0 81 0 4 -3.4208 -2.9058 0 0 71 0 4 -3.058 -2.3496 0 0 72 0 4 -3.7145 -3.4531 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:05:39 81 0 4 -2.8334 -2.3152 0 0 82 0 4 -3.438 -2.9207 0 0 72 0 4 -3.0547 -2.3474 0 0 72 0 4 -3.73 -3.467 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:05:45 81 0 4 -2.854 -2.3343 0 0 83 0 4 -3.4822 -2.9582 0 0 72 0 4 -3.0744 -2.364 0 0 74 0 4 -3.804 -3.5254 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:05:51 82 0 4 -2.8865 -2.3644 0 0 85 0 4 -3.5113 -2.9838 0 0 72 0 4 -3.0894 -2.3756 0 0 75 0 4 -3.8412 -3.5541 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:05:58 82 0 4 -2.8793 -2.3573 0 0 85 0 4 -3.4824 -2.9606 0 0 72 0 4 -3.0826 -2.3676 0 0 76 0 4 -3.8193 -3.5384 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:06:04 83 0 4 -2.8621 -2.3474 0 0 85 0 4 -3.4784 -2.9607 0 0 73 0 4 -3.0871 -2.3709 0 0 75 0 4 -3.784 -3.5124 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:06:10 84 0 4 -2.8574 -2.3415 0 0 86 0 4 -3.4637 -2.9494 0 0 73 0 4 -3.0875 -2.3702 0 0 74 0 4 -3.7445 -3.4836 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:06:17 83 0 4 -2.8568 -2.3419 0 0 86 0 4 -3.4567 -2.9464 0 0 74 0 4 -3.08 -2.3655 0 0 73 0 4 -3.752 -3.4947 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:06:23 85 0 4 -2.8639 -2.3426 0 0 85 0 4 -3.4748 -2.965 0 0 75 0 4 -3.0878 -2.3731 0 0 73 0 4 -3.7552 -3.4926 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:06:29 84 0 4 -2.856 -2.338 0 0 85 0 4 -3.4622 -2.9551 0 0 75 0 4 -3.0751 -2.3645 0 0 75 0 4 -3.7609 -3.4949 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:06:36 83 0 4 -2.8478 -2.3303 0 0 85 0 4 -3.4483 -2.9435 0 0 74 0 4 -3.0448 -2.3422 0 0 73 0 4 -3.732 -3.4745 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:06:42 84 0 4 -2.838 -2.324 0 0 85 0 4 -3.4309 -2.9315 0 0 74 0 4 -3.0282 -2.3314 0 0 72 0 4 -3.7028 -3.4541 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:06:48 83 0 4 -2.8276 -2.3164 0 0 84 0 4 -3.4256 -2.9283 0 0 73 0 4 -3.0364 -2.3386 0 0 73 0 4 -3.7484 -3.4783 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:06:55 84 0 4 -2.8139 -2.3057 0 0 83 0 4 -3.4184 -2.9204 0 0 73 0 4 -3.0546 -2.3558 0 0 73 0 4 -3.7393 -3.4766 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:07:01 84 0 4 -2.7963 -2.2897 0 0 83 0 4 -3.3893 -2.8932 0 0 72 0 4 -3.0602 -2.3621 0 0 73 0 4 -3.7206 -3.4646 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:07:07 82 0 4 -2.7732 -2.2755 0 0 83 0 4 -3.3737 -2.8811 0 0 72 0 4 -3.0539 -2.3574 0 0 72 0 4 -3.7042 -3.4524 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:07:14 81 0 4 -2.7745 -2.2763 0 0 82 0 4 -3.3704 -2.8803 0 0 71 0 4 -3.0538 -2.3564 0 0 74 0 4 -3.7182 -3.4671 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:07:20 82 0 4 -2.7662 -2.2729 0 0 81 0 4 -3.357 -2.8732 0 0 70 0 4 -3.0324 -2.3406 0 0 72 0 4 -3.6973 -3.4541 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:07:27 77 0 4 -2.7668 -2.2728 0 0 80 0 4 -3.3588 -2.8771 0 0 70 0 4 -3.0329 -2.3425 0 0 72 0 4 -3.6937 -3.4567 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:07:33 77 0 4 -2.7669 -2.2747 0 0 79 0 4 -3.3547 -2.8753 0 0 70 0 4 -3.0241 -2.3367 0 0 71 0 4 -3.6833 -3.4457 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:07:39 78 0 4 -2.7652 -2.2716 0 0 79 0 4 -3.3565 -2.8773 0 0 68 0 4 -3.0339 -2.3445 0 0 73 0 4 -3.6956 -3.4433 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:07:45 79 0 4 -2.7775 -2.2818 0 0 78 0 4 -3.3635 -2.8825 0 0 68 0 4 -3.019 -2.3331 0 0 74 0 4 -3.7409 -3.4826 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:07:52 78 0 4 -2.7902 -2.29 0 0 78 0 4 -3.379 -2.8932 0 0 68 0 4 -3.0668 -2.3628 0 0 75 0 4 -3.7425 -3.4794 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:07:58 77 0 4 -2.7842 -2.2863 0 0 77 0 4 -3.3449 -2.8618 0 0 69 0 4 -3.0753 -2.3669 0 0 74 0 4 -3.7178 -3.4592 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 08.11.10 15:08:05 78 0 4 -2.7882 -2.2909 0 0 76 0 4 -3.3442 -2.858 0 0 70 0 4 -3.0893 -2.3769 0 0 75 0 4 -3.7027 -3.4443 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0 ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: simple first emacs script 2010-12-15 12:55 simple first emacs script Tom @ 2010-12-15 14:51 ` Pascal J. Bourguignon 2010-12-15 17:28 ` Tom 0 siblings, 1 reply; 7+ messages in thread From: Pascal J. Bourguignon @ 2010-12-15 14:51 UTC (permalink / raw) To: help-gnu-emacs Tom <tom@somewhere.com> writes: > I am about 2/3 way through the introduction to emacs lisp, and thought > it would help my learning if I wrote tried writing a simple script to > do something useful to me with the lessons I have covered so far so > far. I should point out I am surgeon not a computer scientist and > know nothing more about programming than what is contained in the > introduction to emacs lisp. > I end up with this: > > (defun nirs-data-clean (number-of-channels) > "Cleans the output files from the INVOS monitor removing all columns > apart from StO2 values, and the date and time stamps. Sto2 columns to > be retained are specified by the NUMBER-OF-CHANNELS variable. It > assumes that channels are always used in numerical order, i.e. channel > 1 always, then 2, then 3 then 4." > (interactive "nEnter number of channels (1-4): ") [...] First, unless the indenting was destroyed by your newsreader program, you might want to make use of the indent-region command ( type C-M-\ ) so that your source will look like: (defun nirs-data-clean (number-of-channels) "Cleans the output files from the INVOS monitor removing all columns apart from StO2 values, and the date and time stamps. Sto2 columns to be retained are specified by the NUMBER-OF-CHANNELS variable. It assumes that channels are always used in numerical order, i.e. channel 1 always, then 2, then 3 then 4." (interactive "nEnter number of channels (1-4): ") (barf-if-buffer-read-only) (require 'csv-mode) (push-mark (point-max)) (goto-char (point-min)) (if (or (eq number-of-channels 1) (eq number-of-channels 2) (eq number-of-channels 3) (eq number-of-channels 4)) (or (when (eq number-of-channels 1) (csv-kill-fields '(4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34) (point) (mark))) (when (eq number-of-channels 2) (csv-kill-fields '(4 5 6 7 8 9 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34) (point) (mark))) (when (eq number-of-channels 3) (csv-kill-fields '(4 5 6 7 8 9 11 12 13 14 15 16 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34) (point) (mark))) (when (eq number-of-channels 4) (csv-kill-fields '(4 5 6 7 8 9 11 12 13 14 15 16 18 19 20 21 22 23 25 26 27 28 29 30 31 32 33 34) (point) (mark)) (message "%s is not a valid number of channels. Please enter a number between 1 and 4." number-of-channels)))) (when (y-or-n-p "Replace 0 StO$_2$ values with na: ") (progn (goto-char (point-min)) (while (re-search-forward "[^[:digit:]][0][^[:digit:]]" nil t) (replace-match "na "))))) Instead of calling barf-if-buffer-read-only, you can just prefix the interactive string with a star: (interactive "*nEnter number of channels (1-4): ") The (require 'csv-mode) form would be better placed on the toplevel (ie. above the defun form). Instead of push-mark (I don't see the matching pop-mark), you might use the save-excursion macro. Instead of using eq, you might prefer to use eql by default. In the case of numbers, it would be better to use =. Instead of: (or (when ... ...) (when ... ...) ...) you can write: (cond (... ...) (... ...) ...) And since all your conditions are about the same variable, matching a set of value comparable with eql, you could instead use case: (case number-of-channels ((1) ...) ((2) ...) ((3) ...) ((4) ...) (otherwise (error ...))) Notice how the indentation shows that your message form is not placed at the right level. You intended it to be the else branch of the if form, but you put it inside the last when body. Also you probably want to use error here instead of message, since if the number of channel is wrong, you probably don't want to replace the StO2 zeroes. You may want to use paredit, which helps editing lisp code in a structural way. http://www.emacswiki.org/ParEdit http://www.emacswiki.org/emacs/PareditCheatsheet http://mumble.net/~campbell/emacs/paredit.el Just download this paredit.el file, and put it in /usr/share/emacs/site-lisp/ (check that this path is in load-path, typing C-h v load-path RET. If it is not there, then use the path of the site-lisp directory listed in load-path), then put: (require 'cl) (require 'paredit) (push 'paredit-mode emacs-lisp-mode-hook) (push 'paredit-mode lisp-mode-hook) in your ~/.emacs file. If you want to activate immediately, without restarting emacs, you may reload ~/.emacs with M-x load-file RET ~/.emacs RET Finally, since the bodies of each case branch are the same, you may distribute it around and write: (csv-kill-fields (case number-of-channels ((1) '(4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34)) ((2) '(4 5 6 7 8 9 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34)) ((3) '(4 5 6 7 8 9 11 12 13 14 15 16 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34)) ((4) '(4 5 6 7 8 9 11 12 13 14 15 16 18 19 20 21 22 23 25 26 27 28 29 30 31 32 33 34)) (otherwise (message "%s is not a valid number of channels. Please enter a number between 1 and 4." number-of-channels))) start end) But then, it might be clearer to give an intensional definition instead of an extensional one. A practical operator would be iota: (defun iota (count &optional start step) " RETURN: A list containing the elements (start start+step ... start+(count-1)*step) The start and step parameters default to 0 and 1, respectively. This procedure takes its name from the APL primitive. EXAMPLES: (iota 5) => (0 1 2 3 4) (iota 5 0 -0.1) => (0 -0.1 -0.2 -0.3 -0.4) " (setf start (or start 0) step (or step 1)) (when (< 0 count) (do ((result '()) (item (+ start (* step (1- count))) (- item step))) ((< item start) result) (push item result)))) So you could write: (csv-kill-fields (set-difference (iota 34 1) (ecase number-of-channels ((1) '(1 2 3)) ((2) '(1 2 3 10)) ((3) '(1 2 3 10 17)) ((4) '(1 2 3 10 17 24)))) (point-min) (point-max)) We may remove the otherwise branch, assuming we only get correct number-of-channels (and using ecase instead of case to signal an error if not), which we can ensure by using a more sophisticated interactive form, using competing-read: (interactive (list (first (read-from-string (completing-read "Enter number of channels (1-4): " (mapcar (lambda (x) (cons (format "%d" x) nil)) (iota 4 1)) (function identity) t ; require match "" nil "1" nil))))) (barf-if-buffer-read-only) ; in this case, we need to call it ; separately. Be sure to read the documentation of each operator used, eg. with: C-h f interactive RET C-h f completing-read RET etc. when (and unless and other macros) has an implicit progn, so there's no need to embed one in it. So to wrap it up so far, you could have: (defun nirs-data-clean (number-of-channels) "Cleans the output files from the INVOS monitor removing all columns apart from StO2 values, and the date and time stamps. Sto2 columns to be retained are specified by the NUMBER-OF-CHANNELS variable. It assumes that channels are always used in numerical order, i.e. channel 1 always, then 2, then 3 then 4." (interactive (list (first (read-from-string (completing-read "Enter number of channels (1-4): " (mapcar (lambda (x) (cons (format "%d" x) nil)) (iota 4 1)) (function identity) t ; require match "" nil "1" nil))))) (barf-if-buffer-read-only) (save-excursion (let ((start (point-min)) (end (point-max))) (goto-char start) (csv-kill-fields (set-difference (iota 34 1) (ecase number-of-channels ((1) '(1 2 3)) ((2) '(1 2 3 10)) ((3) '(1 2 3 10 17)) ((4) '(1 2 3 10 17 24)))) start end) (when (y-or-n-p "Replace 0 StO$_2$ values with na: ") (goto-char start) (while (re-search-forward "[^[:digit:]][0][^[:digit:]]" nil t) (replace-match "na ")))))) > The idea is to automatically clean unwanted columns from csv (space > separated) data file and ask me if I want to replace 0 values with > na. Ah, if you read the documentation of csv-kill-fields, you will see that it depends on the right setting of the variable csv-separators to know what separator to use. By default I have it set to a comma. So you want to bind this variable in your function: (let ((csv-separators '(" "))) (csv-kill-fields ...)) Note however that it is a single character string, and that your fields are separated by several. When I try it, it fails with csv-kill-fields complaining about the number of columns. It is probably better to use commas to separate the fields, so: (defun nirs-data-clean (number-of-channels) "Cleans the output files from the INVOS monitor removing all columns apart from StO2 values, and the date and time stamps. Sto2 columns to be retained are specified by the NUMBER-OF-CHANNELS variable. It assumes that channels are always used in numerical order, i.e. channel 1 always, then 2, then 3 then 4." (interactive (list (first (read-from-string (completing-read "Enter number of channels (1-4): " (mapcar (lambda (x) (cons (format "%d" x) nil)) (iota 4 1)) (function identity) t ; require match "" nil "1" nil))))) (barf-if-buffer-read-only) (save-excursion (replace-regexp " +" "," nil (point-min) (point-max)) (goto-char (point-min)) (let ((csv-separators '(","))) (csv-kill-fields (set-difference (iota 34 1) (ecase number-of-channels ((1) '(1 2 3)) ((2) '(1 2 3 10)) ((3) '(1 2 3 10 17)) ((4) '(1 2 3 10 17 24)))) (point-min) (point-max))) (when (y-or-n-p "Replace 0 StO$_2$ values with na: ") (goto-char (point-min)) (while (re-search-forward "[^[:digit:]]0[^[:digit:]]" nil t) (replace-match "na "))) ;; let's put back spaces as separators: (replace-regexp "," " " nil (point-min) (point-max)))) > I appreciate this is very messy, and I should be able to specify > ranges of field rather than listing every single one (but I couldn't > get that to work), but it works kind of. When I evaluate it and run > it for the first time it works, but if I undo the changes to the file > and run it again it often only takes out the 4th column, if I reopen > the file or re-evaluate the script it usually works again but I can't > seem to work the pattern of working and not working - as I can't see > the pattern I can't learn where I am going wrong. I see no reason why that would happen. In any case, my version seems to work correctly, even after undoing and running it again. > I assume I have made an obvious mistake but I don't have the > experience to know what it is. Any general comments about > simple/better ways to clean this script would be welcome, for example > would I be better making the regexp replace a yes/no arg to the > function so if I call this script from another script I can specify a > y/n argument (I don't know how to do this anyway though). Indeed, this is a good intension. You can separate interactive user interface commands from the functions doing the actual work, so that you can reuse the later. The interactive form allow you to merge both usages. You only have to add the wanted parameter, and specify it in the interactive form. You can also specify some parameters optional: (defun nirs-data-clean (number-of-channels &optional replace-StO2-0-p) ...) The simple interactive form would use a multiline string: (interactive "*nEnter number of channels (1-4): sReplace 0 StO$_2$ values with na (y/n): ") Unfortunately, interactive doesn't provide for a 'boolean' input, so we'll keep using the sophisticated form: (defun nirs-data-clean (number-of-channels &optional replace-StO2-0-p) "Cleans the output files from the INVOS monitor removing all columns apart from StO2 values, and the date and time stamps. Sto2 columns to be retained are specified by the NUMBER-OF-CHANNELS variable. It assumes that channels are always used in numerical order, i.e. channel 1 always, then 2, then 3 then 4." (interactive (list (first (read-from-string (completing-read "Enter number of channels (1-4): " (mapcar (lambda (x) (cons (format "%d" x) nil)) (iota 4 1)) (function identity) t ; require match "" nil "1" nil))) (string= "yes" (completing-read "Replace 0 StO$_2$ values with na: " (mapcar (lambda (x) (cons x nil)) '("yes" "no")) (function identity) t ; require match "" nil "yes" nil)))) (barf-if-buffer-read-only) (save-excursion (replace-regexp " +" "," nil (point-min) (point-max)) (goto-char (point-min)) (let ((csv-separators '(","))) (csv-kill-fields (set-difference (iota 34 1) (ecase number-of-channels ((1) '(1 2 3)) ((2) '(1 2 3 10)) ((3) '(1 2 3 10 17)) ((4) '(1 2 3 10 17 24)))) (point-min) (point-max))) (when replace-StO2-0-p (goto-char (point-min)) (while (re-search-forward "[^[:digit:]]0[^[:digit:]]" nil t) (replace-match "na "))) ;; let's put back spaces as separators: (replace-regexp "," " " nil (point-min) (point-max)))) Finally, when you'll have completed the "Introduction to Emacs Lisp", I would advise you to read SICP. SICP = Structure and Interpretation of Computer Programs http://mitpress.mit.edu/sicp/full-text/book/book-Z-H-4.html http://swiss.csail.mit.edu/classes/6.001/abelson-sussman-lectures/ http://www.codepoetics.com/wiki/index.php?title=Topics:SICP_in_other_languages http://eli.thegreenplace.net/category/programming/lisp/sicp/ http://www.neilvandyke.org/sicp-plt/ http://www.youtube.com/watch?v=rdj6deraQ6k (and watch the videos, they're quite instructive). While this book uses scheme, it's subject matter is not about scheme, but programming in general, as can be seen from the translations of the exercises in other programming languages (well, it seems the wiki has been defaced, so it's not available hopefully temporarily, but eli's blog contains Common Lisp versions of the exercises, which are close to emacs lisp). -- __Pascal Bourguignon__ http://www.informatimago.com/ A bad day in () is better than a good day in {}. ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: simple first emacs script 2010-12-15 14:51 ` Pascal J. Bourguignon @ 2010-12-15 17:28 ` Tom 2010-12-15 18:37 ` Pascal J. Bourguignon 0 siblings, 1 reply; 7+ messages in thread From: Tom @ 2010-12-15 17:28 UTC (permalink / raw) To: help-gnu-emacs Wow Pascal that is quite an amazing response thanks. You introduced several new things I don't know about so I can't comment on them until I go away and learn them but in response to what I do understand. Yes the indentation was destroyed by newsreader, but thanks for pointing me to paredit as I was finding managing parenthesis a pain. > The (require 'csv-mode) form would be better placed on the toplevel > (ie. above the defun form). I don't get this. If I understand you correctly you are suggesting something like this: (require 'csv-mode) (defun ... ) If I do this then wont the require mode cease to be part of the functions definition. Normally it would not be required to set the mode the csv as the file extension would be .csv and csv-mode is called automatically, but the raw files I receive have random extensions - I suppose I could rename them all to overcome this but it seemed simpler to tell the function to go into csv mode otherwise it tries to process the file in fundamental mode. > Instead of push-mark (I don't see the matching pop-mark), you might use > the save-excursion macro. I did actually start with save-excursion but I have no interest in saving the point the mark, the whole point of pushing the mark and moving the point to the start of the buffer was to specify the region arguments in csv-kill-fields, i.e. (csv-kill-fields '(4 ...) (point) (mark)) I guess this might be more logically done with (csv-kill-fields '(4 ...) (point-min) (point-max)) would that be considered better form? Thanks for the advice on eql cond, case, and distribution of my column specifiers that will clean things up a bit. I don't know enough to follow your iota operator but that gives me something to work towards > when (and unless and other macros) has an implicit progn, so there's no > need to embed one in it. That is handy to know. > Ah, if you read the documentation of csv-kill-fields, you will see that > it depends on the right setting of the variable csv-separators to know > what separator to use. By default I have it set to a comma. So you > want to bind this variable in your function: > > (let ((csv-separators '(" "))) > (csv-kill-fields ...)) > Note however that it is a single character string, and that your fields > are separated by several. When I try it, it fails with csv-kill-fields > complaining about the number of columns. It is probably better to use > commas to separate the fields, ... I have read the documentation (that doesn't mean I understood it though). I have the csv separators specified in my .emacs file (it seem it will accept both " " and "," so csv-modes seems to read my files correctly. But I guess it is logical to specify these in the function in case I run it on a computer without these specified. I don't seem to get problems with csv-kill-fields complaining about number of columns but maybe I have just worked through it with trial and error and no real understanding. > Indeed, this is a good intension. You can separate interactive user > interface commands from the functions doing the actual work, so that you > can reuse the later. The interactive form allow you to merge both > usages. > > You only have to add the wanted parameter, and specify it in the > interactive form. You can also specify some parameters optional: > > > (defun nirs-data-clean (number-of-channels&optional replace-StO2-0-p) > ...) > > The simple interactive form would use a multiline string: > > (interactive "*nEnter number of channels (1-4): > sReplace 0 StO$_2$ values with na (y/n): ") > > Unfortunately, interactive doesn't provide for a 'boolean' input, so > we'll keep using the sophisticated form: When I said I can't do this I was looking to specify boolean input, good to know I shouldn't waste my time looking for it. I could have probably worked it out the way you showed but it would have probably taken me a few hours of testing so thanks for demonstrating that. Your final script seems more solid than mine, as in it behaves in the same way on either iteration even after undoing. It doesn't seem to work perfectly with across all channel options in some of the files I ran it one, but there are lots of ideas in there (such as temporarily using commas) for me to incorporate into my script so thanks again. ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: simple first emacs script 2010-12-15 17:28 ` Tom @ 2010-12-15 18:37 ` Pascal J. Bourguignon 2010-12-16 15:21 ` Tom 2010-12-16 21:55 ` Stefan Monnier 0 siblings, 2 replies; 7+ messages in thread From: Pascal J. Bourguignon @ 2010-12-15 18:37 UTC (permalink / raw) To: help-gnu-emacs Tom <tom@somewhere.com> writes: > Wow Pascal that is quite an amazing response thanks. > > You introduced several new things I don't know about so I can't > comment on them until I go away and learn them but in response to what > I do understand. > > Yes the indentation was destroyed by newsreader, but thanks for > pointing me to paredit as I was finding managing parenthesis a pain. > >> The (require 'csv-mode) form would be better placed on the toplevel >> (ie. above the defun form). > > I don't get this. If I understand you correctly you are suggesting > something like this: > (require 'csv-mode) > (defun ... > ) > > If I do this then wont the require mode cease to be part of the > functions definition. Normally it would not be required to set the > mode the csv as the file extension would be .csv and csv-mode is > called automatically, but the raw files I receive have random > extensions - I suppose I could rename them all to overcome this but it > seemed simpler to tell the function to go into csv mode otherwise it > tries to process the file in fundamental mode. require only loads a library (if it is not already loaded). Setting a mode for a buffer would be done by calling the specific mode command, which may be defined in a library: (csv-mode) (By the way, these commands, as well as a lot of rarely commands, may be not loaded initially, but defined as autoloaded functions, that will load the library that contains their real definition automatically the first time they're called. For example, if you start emacs again, before loading csv-mode.el, the documentation of the command csv-mode (C-x f csv-mode RET) is: csv-mode is an interactive autoloaded Lisp function in `csv-mode.el'. [Arg list not available until function definition is loaded.] Major mode for editing comma-separated value files. ) Loading csv-mode with (require 'csv-mode) doesn't change the mode of the buffer, and this doesn't prevent csv-kill-fields to work, even if the mode of the buffer is not csv-mode. In general, modes only establish the key bindings and font-lock keywords to help editing a specific kind of text, but all the commands are applyable in all the modes. There may be some special modes that do some behind the scene processing (eg. building data structures in parallel to the buffer, defining buffer-local variables) that would be required by some of their commands for them to work, but it's rather rare. But, I would advise to avoid changing the mode of the buffer in a command such as nirs-data-clean. If you want to open the .R14 files in the CSV mode, you can do it by adding an entry to the auto-mode-alist variable (C-h v auto-mode-alist RET) in ~/.emacs: (push '("\\.R14$" . csv-mode) auto-mode-alist) However, the R14 example file you gave is not a CSV formated file. See below. >> Instead of push-mark (I don't see the matching pop-mark), you might use >> the save-excursion macro. > > I did actually start with save-excursion but I have no interest in > saving the point the mark, the whole point of pushing the mark and > moving the point to the start of the buffer was to specify the region > arguments in csv-kill-fields, i.e. > (csv-kill-fields '(4 ...) (point) (mark)) > > I guess this might be more logically done with > (csv-kill-fields '(4 ...) (point-min) (point-max)) > would that be considered better form? Yes, I noticed later your use of the (point) and (mark). But indeed, if you follow the documentation of push-mark, you'll see in the documentation of set-mark that this mechanism is reserved to the user interactive use, and that commands should avoid tampering with it. At first, I kept (point-min) and (point-max) in local variables start and end, but since I added the replacement of spaces by comma, this changed the size of the buffer, and therefore the value of (point-max). Therefore I called these functions everytime. If you want to memorize a buffer position in the course of editing that may change its absolute position (insertion and deletions), you may use markers (see the functions make-marker and set-marker), but markers need to be 'freed' explicitely by reseting them to nil, so they're less convenient to use than just calling (point-max) again (but to keep a position in the middle of the buffer they'd be the right mechanism to use). >> Ah, if you read the documentation of csv-kill-fields, you will see that >> it depends on the right setting of the variable csv-separators to know >> what separator to use. By default I have it set to a comma. So you >> want to bind this variable in your function: >> >> (let ((csv-separators '(" "))) >> (csv-kill-fields ...)) >> Note however that it is a single character string, and that your fields >> are separated by several. When I try it, it fails with csv-kill-fields >> complaining about the number of columns. It is probably better to use >> commas to separate the fields, ... > > I have read the documentation (that doesn't mean I understood it > though). I have the csv separators specified in my .emacs file (it > seem it will accept both " " and "," so csv-modes seems to read my > files correctly. Well, I'm not sure if it's a good idea to have both in the csv-separators list. One would have to check how csv functions deal with csv-separators. For example, I wonder what would happen if a record contained both separators: data item,other data, item It this ("data" "item,other" "data," "item") or ("data item" "other data" " item") or ("data" "item" "other" "data" "item") ? > But I guess it is logical to specify these in the > function in case I run it on a computer without these specified. I > don't seem to get problems with csv-kill-fields complaining about > number of columns but maybe I have just worked through it with trial > and error and no real understanding. I was surprised by this result too, given my reading of the documentation of csv-kill-fields. > Your final script seems more solid than mine, as in it behaves in the > same way on either iteration even after undoing. It doesn't seem to > work perfectly with across all channel options in some of the files I > ran it one, but there are lots of ideas in there (such as temporarily > using commas) for me to incorporate into my script so thanks again. Perhaps the problem comes from the fact that the files don't look like csv files really. They seem to have fixed-width columns, filled with spaces. In a csv file, if the separator character is present consecutively, that would mean that there is an empty field in between. Aligning data with a variable number of spaces is therefore incompatible. Perhaps some of your files have fields with spaces in the middle, or empty fields. Then simply replacing sequences of spaces by comma to make it csv (or have csv function interpret the space as a field separator) will make the csv function interpret incorrectly the fields. I would advise to check the specifications of the file format, and perhaps use a different code to convert it to csv. For example, assuming we have just records of fixed-width fields. (defun spacep (ch) (= ch ?\ )) ; one space character. (let ((one-record "08.11.10 14:57:17 67 0 4 -2.9254 -2.3866 0 0 72 0 4 -3.3003 -2.7971 0 0 63 0 4 -2.8989 -2.2108 0 0 75 0 4 -3.6963 -3.3294 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0")) (loop ; let's detect a data -> space transition with data = nil with fields = '() for pos from 0 for ch across one-record do (if data (when (spacep ch) (setf data nil) (push pos fields)) (unless (spacep ch) (setf data t))) finally (return (cons 0 (reverse (cons (length one-record) fields)))))) --> (0 8 17 21 26 30 43 56 59 63 67 72 76 89 102 105 109 113 118 122 135 148 151 155 159 164 168 181 194 197 201 217 233 249 265) So you could now split the record in fields, remove the spaces, and concatenate it back into a csv record: (defvar *r14-field-positions* '(0 8 17 21 26 30 43 56 59 63 67 72 76 89 102 105 109 113 118 122 135 148 151 155 159 164 168 181 194 197 201 217 233 249 265)) (defun csvify-r14-record (record) (unsplit-string (mapcar (lambda (field) ; if the field contains a comma, ; it needs to be quoted. (if (find ?, field) (concat "\"" (replace-regexp-in-string "\"" "\\\"" field) "\"") field)) (loop for (start end) on *r14-field-positions* while end collect (string-trim " " (subseq record start end)))) ",")) (let ((one-record "08.11.10 14:57:17 67 0 4 -2.9254 -2.3866 0 0 72 0 4 -3.3003 -2.7971 0 0 63 0 4 -2.8989 -2.2108 0 0 75 0 4 -3.6963 -3.3294 0 0 AB0912040885-0 AB0912040757-0 AB0912040628-0 AB0912040780-0")) (csvify-r14-record one-record)) --> "08.11.10,14:57:17,67,0,4,-2.9254,-2.3866,0,0,72,0,4,-3.3003,-2.7971,0,0,63,0,4,-2.8989,-2.2108,0,0,75,0,4,-3.6963,-3.3294,0,0,AB0912040885-0,AB0912040757-0,AB0912040628-0,AB0912040780-0" So now we only have to call this function on each line of the buffer: (defun csvify-r14-buffer () (interactive) (dolines (start-line end-line) (let ((new-record (csvify-r14-record (buffer-substring start-line end-line)))) (delete-region start-line end-line) (insert new-record)))) With the following functions and macro (from my personal library): (defun string-trim (character-bag string-designator) "Common-Lisp: returns a substring of string, with all characters in \ character-bag stripped off the beginning and end. " (unless (sequencep character-bag) (signal 'type-error "Expected a sequence for `character-bag'.")) (let* ((string (string* string-designator)) (margin (format "[%s]*" (regexp-quote (if (stringp character-bag) character-bag (map 'string 'identity character-bag))))) (trimer (format "\\`%s\\(\\(.\\|\n\\)*?\\)%s\\'" margin margin))) (replace-regexp-in-string trimer "\\1" string))) (defun unsplit-string (string-list &rest separator) "Does the inverse than split-string. If no separator is provided then a simple space is used." (if (null separator) (setq separator " ") (if (= 1 (length separator)) (setq separator (car separator)) (error "unsplit-string: Too many separator arguments."))) (if (not (char-or-string-p separator)) (error "unsplit-string: separator must be a string or a char.")) (apply 'concat (list-insert-separator string-list separator))) (defmacro* with-marker ((var position) &body body) (let ((vposition (gensym))) ; so (eq var position) still works. `(let* ((,vposition ,position) (,var (make-marker))) (set-marker ,var ,vposition) (unwind-protect (progn ,@body) (set-marker ,var nil))))) (defmacro* dolines (start-end &body body) "Executes the body with start-var and end-var bound to the start \ and the end of each lines of the current buffer in turn." (let ((vline (gensym))) (destructuring-bind (start-var end-var) start-end `(let ((sm (make-marker)) (em (make-marker))) (unwind-protect (progn (goto-char (point-min)) (while (< (point) (point-max)) (let ((,vline (point))) (set-marker sm (point)) (set-marker em (progn (end-of-line) (point))) (let ((,start-var (marker-position sm)) (,end-var (marker-position em))) ,@body) (goto-char ,vline) (forward-line 1)))) (set-marker sm nil) (set-marker em nil)) nil)))) So instead of the replace-regexp, you can use (csvify-r14-buffer). At the end, you didn't say what resulting file format you wanted. You could remove the last replace-regexp, and keep the result in csv format, keep it, and have fields containing commas be left quoted (but you don't seem to have such data anyways), or write a more sophisticated command to format csv records into whatever format you want. -- __Pascal Bourguignon__ http://www.informatimago.com/ A bad day in () is better than a good day in {}. ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: simple first emacs script 2010-12-15 18:37 ` Pascal J. Bourguignon @ 2010-12-16 15:21 ` Tom 2010-12-16 21:55 ` Stefan Monnier 1 sibling, 0 replies; 7+ messages in thread From: Tom @ 2010-12-16 15:21 UTC (permalink / raw) To: help-gnu-emacs Pascal, Thanks again for this, there is plenty for me to build upon and learn from here. I will go away and ponder it for a while. Tom ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: simple first emacs script 2010-12-15 18:37 ` Pascal J. Bourguignon 2010-12-16 15:21 ` Tom @ 2010-12-16 21:55 ` Stefan Monnier 2010-12-17 22:13 ` Thien-Thi Nguyen 1 sibling, 1 reply; 7+ messages in thread From: Stefan Monnier @ 2010-12-16 21:55 UTC (permalink / raw) To: help-gnu-emacs > If you want to memorize a buffer position in the course of editing that > may change its absolute position (insertion and deletions), you may use > markers (see the functions make-marker and set-marker), but markers need > to be 'freed' explicitely by reseting them to nil, Actually, they don't need to be freed explicitly (it may sometimes be beneficial to performance, but only if there are many of them). BTW, instead of using markers, you can often avoid the problem altogether by simply performing the modifications starting from the end of the region (so while the (point-max) you've computed at the beginning is not valid at the end any more, it doesn't matter because you're done processing that part of the buffer). Stefan ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: simple first emacs script 2010-12-16 21:55 ` Stefan Monnier @ 2010-12-17 22:13 ` Thien-Thi Nguyen 0 siblings, 0 replies; 7+ messages in thread From: Thien-Thi Nguyen @ 2010-12-17 22:13 UTC (permalink / raw) To: help-gnu-emacs () Stefan Monnier <monnier@iro.umontreal.ca> () Thu, 16 Dec 2010 16:55:10 -0500 modifications starting from the end An example of this is ‘vc-rcs-annotate-command’ (vc-rcs.el). Specifically, look for the comment that contains "DTRT". ^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2010-12-17 22:13 UTC | newest] Thread overview: 7+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2010-12-15 12:55 simple first emacs script Tom 2010-12-15 14:51 ` Pascal J. Bourguignon 2010-12-15 17:28 ` Tom 2010-12-15 18:37 ` Pascal J. Bourguignon 2010-12-16 15:21 ` Tom 2010-12-16 21:55 ` Stefan Monnier 2010-12-17 22:13 ` Thien-Thi Nguyen
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).