You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Idris实现十六进制字符串证明函数及问题求助

Hey there! Let's tackle your two questions about verifying hex strings and building proof functions in Idris step by step.

1. How to Prove a String is Hexadecimal Format?

Based on your requirements (strings starting with 0x followed by an even number of 0-9/a-f characters), we can use dependent types to formalize this:

  • First, define IsNib to prove a single character is a valid hex nibble.
  • Then, define IsHex to prove the entire string follows the 0x + even nibbles pattern.

Your initial type definitions are on the right track—we just need to adjust them slightly to make writing automatic proof functions easier.

2. Building the Automatic Proof Function in Idris

Let's break down the two pain points you're facing and fix them one by one.

Problem 1: Simplifying isItNib : Char -> Dec (IsNib c)

Writing negation proofs for every non-hex character is tedious. Instead of handling each invalid case manually, we can use a helper function to extract the character from an IsNib proof, then use decEq to generate the contradiction automatically.

First, keep your original IsNib definition (since it's explicit and clear), then add this helper to reduce repetition:

data IsNib : Char -> Type where
  IsNib0 : IsNib '0'
  IsNib1 : IsNib '1'
  IsNib2 : IsNib '2'
  IsNib3 : IsNib '3'
  IsNib4 : IsNib '4'
  IsNib5 : IsNib '5'
  IsNib6 : IsNib '6'
  IsNib7 : IsNib '7'
  IsNib8 : IsNib '8'
  IsNib9 : IsNib '9'
  IsNibA : IsNib 'a'
  IsNibB : IsNib 'b'
  IsNibC : IsNib 'c'
  IsNibD : IsNib 'd'
  IsNibE : IsNib 'e'
  IsNibF : IsNib 'f'

-- Helper to get the character from an IsNib proof
nibChar : IsNib c -> Char
nibChar IsNib0 = '0'
nibChar IsNib1 = '1'
nibChar IsNib2 = '2'
nibChar IsNib3 = '3'
nibChar IsNib4 = '4'
nibChar IsNib5 = '5'
nibChar IsNib6 = '6'
nibChar IsNib7 = '7'
nibChar IsNib8 = '8'
nibChar IsNib9 = '9'
nibChar IsNibA = 'a'
nibChar IsNibB = 'b'
nibChar IsNibC = 'c'
nibChar IsNibD = 'd'
nibChar IsNibE = 'e'
nibChar IsNibF = 'f'

isItNib : (c : Char) -> Dec (IsNib c)
isItNib '0' = Yes IsNib0
isItNib '1' = Yes IsNib1
isItNib '2' = Yes IsNib2
isItNib '3' = Yes IsNib3
isItNib '4' = Yes IsNib4
isItNib '5' = Yes IsNib5
isItNib '6' = Yes IsNib6
isItNib '7' = Yes IsNib7
isItNib '8' = Yes IsNib8
isItNib '9' = Yes IsNib9
isItNib 'a' = Yes IsNibA
isItNib 'b' = Yes IsNibB
isItNib 'c' = Yes IsNibC
isItNib 'd' = Yes IsNibD
isItNib 'e' = Yes IsNibE
isItNib 'f' = Yes IsNibF
-- For invalid chars, use the helper to generate a contradiction
isItNib c = No (\proof => absurd $ decEq c (nibChar proof))

This cuts down the negation code to a single line—no more repetitive case handling for every invalid character!

Problem 2: Implementing Total isItHex : String -> Dec (IsHex s)

Your original IsHex definition is based on string concatenation, which makes parsing tricky (since we process strings left-to-right). Let's redefine IsHex to align with how we actually check strings: first confirm it starts with 0x, then verify the rest has an even number of valid nibbles.

Step 1: Redefine the Types

-- Helper type: proves a string is an even number of valid hex nibbles
data HexTail : String -> Type where
  HexTailEmpty : HexTail ""
  HexTailPair : IsNib c1 -> IsNib c2 -> HexTail rest -> HexTail (singleton c1 ++ singleton c2 ++ rest)

-- Main IsHex type: starts with "0x", followed by a valid HexTail
data IsHex : String -> Type where
  MkIsHex : HexTail tail -> IsHex ("0x" ++ tail)

This structure makes parsing straightforward—we split the string at the first two characters to check for 0x, then process the rest in pairs.

Step 2: Implement the Decision Functions

isItHexTail : (s : String) -> Dec (HexTail s)
isItHexTail "" = Yes HexTailEmpty
isItHexTail s with (strSplitAt 2 s)
  -- Case 1: We can split into two characters + rest
  isItHexTail (c1 :: c2 :: rest) | (MkPair (c1 :: c2 :: []) rest) =
    case (isItNib c1, isItNib c2) of
      (Yes nib1, Yes nib2) =>
        case isItHexTail rest of
          Yes restProof => Yes (HexTailPair nib1 nib2 restProof)
          No contra => No (\(HexTailPair _ _ p) => contra p)
      (No contra1, _) => No (\(HexTailPair nib1 _ _) => contra1 nib1)
      (_, No contra2) => No (\(HexTailPair _ nib2 _) => contra2 nib2)
  -- Case 2: String length is odd (can't split into pairs)
  isItHexTail _ | _ = No (\proof => case proof of
    HexTailEmpty => absurd $ decEq s ""
    HexTailPair _ _ _ => absurd $ length s `mod` 2 = 1)

isItHex : (s : String) -> Dec (IsHex s)
isItHex s with (strSplitAt 2 s)
  -- Case 1: String starts with "0x"
  isItHex ("0x" ++ tail) | (MkPair "0x" tail) =
    case isItHexTail tail of
      Yes tailProof => Yes (MkIsHex tailProof)
      No contra => No (\(MkIsHex p) => contra p)
  -- Case 2: String doesn't start with "0x" (or is shorter than 2 chars)
  isItHex _ | _ = No (\(MkIsHex _) => absurd $ decEq (take 2 s) "0x")

Key Fixes:

  • Type Mismatch: By redefining IsHex to use prefix checking instead of suffix concatenation, we eliminate the type mismatch when handling empty strings or invalid prefixes.
  • Total Function: Using with clauses to cover all possible string cases (starts with 0x or not, even/odd length in the tail) ensures the function is total. Each No branch provides a clear contradiction proof, so Idris recognizes it covers all cases.

Testing the Function

Now you can generate proofs automatically:

-- Valid hex string: gets a proof
validHex : IsHex "0x1a7f33b8"
validHex = case isItHex "0x1a7f33b8" of
  Yes p => p
  No _ => impossible

-- Invalid character: returns No
invalidChar : Dec (IsHex "0x1g")
invalidChar = isItHex "0x1g"

-- Odd number of nibbles after 0x: returns No
oddLength : Dec (IsHex "0x1")
oddLength = isItHex "0x1"

内容的提问来源于stack exchange,提问作者MaiaVictor

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:30:45