我有一个天气获取应用程序,我正在努力,我有一些关于列表的麻烦 . 我使用openweathermap和convert-to-json从我的get-weather函数返回以下列表:
((:COORD (:LON . -123.12) (:LAT . 49.25))
(:WEATHER
((:ID . 500) (:MAIN . "Rain") (:DESCRIPTION . "light rain") (:ICON . "10n")))
(:BASE . "cmc stations")
(:MAIN (:TEMP . 281.56) (:PRESSURE . 1001) (:HUMIDITY . 93)
(:TEMP--MIN . 276.15) (:TEMP--MAX . 283.15))
(:WIND (:SPEED . 3.1) (:DEG . 100)) (:CLOUDS (:ALL . 90)) (:DT . 1453467600)
(:SYS (:TYPE . 1) (:ID . 3359) (:MESSAGE . 0.0039) (:COUNTRY . "CA")
(:SUNRISE . 1453478139) (:SUNSET . 1453510389))
(:ID . 6173331) (:NAME . "Vancouver") (:COD . 200))
而我正试图访问:天气:大雨 . 目前我在做:
(cdr (second (second (assoc :weather *assoc-list-from-above*))))
有没有更好的办法?
4 回答
如果您经常使用这些值,您可能希望将alist转换为CLOS对象 . json-library可能有一些东西可以帮助你做到这一点,但这里是一个手动执行天气部分的例子 . 您可以类似地完成其余的工作 .
Edit: 添加到其他选项:您可以查看json-bind实用程序 .
从约书亚借来
*input*
:Another edit: 如果您有多个
weather
,您最好的选择是使用CLOS(我的选项或Joshuas) . 如果你还需要使用天气以外的其他领域,你可以结合我给出的两个解决方案:如果您不想使用CLOS,也可以这样做:
虽然我非常喜欢@ jkiiski的
json-bind
解决方案,但我想我也会添加以下选项 .If the query path is known at compile-time ,您可以使用以下宏
例子:
(report-get *array-from-above* :weather 0 :main)
中的0
是访问天气项目集合中的第一项Edit :忘了提 - 这个宏是
setf
-able .可能对您的要求没有用,但很高兴知道 .
首先,使用一些中间变量使访问关联列表更加清晰可能就足够了 . 首先,让我们定义一个我们可以解析的输入字符串(将来,请尝试在问题中提供这些字符串,因为它们可以帮助其他人提供答案):
现在,在我看来,你可以更清洁地提取天气场主场的 Value :
如果你像这样重复调用 (cdr (assoc …)) ,那么基于提供的路径执行此操作的函数将有很大帮助,如Rainer's answer所示 . 当然,数组索引(例如,您想要天气列表的第一个元素)会使事情变得不那么干净 .
现在,您还要解码为CLOS实例 . CL-JSON可以将JSON解码为匿名CLOS类的实例 . 单独这样做会改变访问权限,但不会太多 . 尽管如此,它使得该领域的工作方式更加清晰 . 请注意,由于天气值现在是一个数组,而不是一个列表,我们得到的第一个元素是 (aref array 0) ,而不是 (first list) .
现在,我认为使用CLOS类的真正好处是您可以定义自己的类,然后使用change-class将CL-JSON提供的实例更改为您自己的类的实例 . 在代码中定义类也有助于文档 . 对于一个小例子来说,这似乎不是什么大不了的事,但在编写可维护的代码时,它是一个可行的类定义 . 请注意,人们对命名约定有不同的看法(例如,是否使用 wreport-weather 作为访问者或 weather ) .
现在您可以使用 change-class 将您的对象转换为 wreport ,然后您可以使用 wreport-weather (以及 aref ,因为该值仍然是数组)来获取子对象,然后您可以使用 slot-value (如上所示)来获取主要领域:
为天气元素定义一个子类可能是有意义的,这并不太难 . 既然我们调用了顶级的东西 wreport ,我们可以调用低级别的东西 subreport :
现在,唯一要做的就是在我们使用 change-class 将顶级报告更改为 wreport 的实例后,我们需要为其天气数组中的每个元素调用 change-class 以将其转换为 subreport . 根据change-class的文档,调用update-instance-for-different-class . 我们可以定义一个 :after 方法来为我们做转换:
如果你没有't done much with CLOS, that might be a bit intimidating, but you'基本上说“在 change-class 完成所有工作之后,还要再做一次转换 . 现在你可以在两个级别使用域适当的访问器:
这可能看起来要做很多工作,对于一个快速的小脚本,它可能很好 . 但是,如果您需要这些结构一段时间,将文档编入代码中会很有帮助 . 如果您需要构建任何天气报告,拥有一个好的域模型将会有很大帮助 .
您可以编写一个函数,在其中传递访问路径:
路径中的元素可以是键(键提取器)的键或列表 . 提取器需要是一个从返回的关联列表项中提取数据的函数 .
例:
任务:
让它更健壮等
处理不同的列表:关联列表,属性列表,带项目的列表,...